c++98/c++03 constructor overloading

  • Follow


Hi,

With gcc-4.2 and earlier, and with subsequent versions of gcc when
using the -std=c++98 option, the following code fails to compile:

--------------------------

#include <iostream>
#include <ostream>

enum ByRef {by_ref};

template <class T>
class MyClass {
  T t;
public:
  MyClass(const T& arg): t(arg) {                  // (1)
    std::cout << "Without ByRef tag" << std::endl;
  }
  MyClass(T arg, ByRef): t(arg) {                  // (2)
    std::cout << "With ByRef tag" << std::endl;
  }
};

int main() {
  int i = 0;
  MyClass<int&> m2(i, by_ref);
}

--------------------------

gcc ignores the ByRef tag, and picks constructor 1, and then fails to
compile because of the resulting reference to a reference.

Without the -std=c++98 option, gcc-4.3 onwards pick the right overload
(constructor 2), as does Comeau in strict c++03 mode.

Is this a gcc bug or do c++98 overload rules compel the tag
enumeration to be ignored and the incorrect constructor to be called?
If c++98 overload rules compel this behaviour, at what point did it
change (is it a c++03 thing, or in anticipation of c++0x?)

Chris
0
Reply Chris 12/7/2010 12:00:02 PM

Chris Vine wrote:

> Hi,
> 
> With gcc-4.2 and earlier, and with subsequent versions of gcc when
> using the -std=c++98 option, the following code fails to compile:
> 
> --------------------------
> 
> #include <iostream>
> #include <ostream>
> 
> enum ByRef {by_ref};
> 
> template <class T>
> class MyClass {
>   T t;
> public:
>   MyClass(const T& arg): t(arg) {                  // (1)
>     std::cout << "Without ByRef tag" << std::endl;
>   }
>   MyClass(T arg, ByRef): t(arg) {                  // (2)
>     std::cout << "With ByRef tag" << std::endl;
>   }
> };
> 
> int main() {
>   int i = 0;
>   MyClass<int&> m2(i, by_ref);
> }
> 
> --------------------------
> 
> gcc ignores the ByRef tag, and picks constructor 1, and then fails to
> compile because of the resulting reference to a reference.
> 

No, It's an error right way when you implicitly instantiate MyClass<int&>. 
Try

sizeof(MyClass<int&>);

On a literal C++03 compiler, this *must* result in a compile error because 
the declaration of the destructor is invalid. You don't even get to the 
point where a constructor could be chosen by overload resolution.

> Without the -std=c++98 option, gcc-4.3 onwards pick the right overload
> (constructor 2), as does Comeau in strict c++03 mode.
> 

More recent compilers implement this according to various C++ DR 
resolutions. 

0
Reply Johannes 12/7/2010 12:31:00 PM


On Tue, 07 Dec 2010 13:31 +0100
"Johannes Schaub (litb)" <schaub-johannes@web.de> wrote:
> Chris Vine wrote:
> 
> > Hi,
> > 
> > With gcc-4.2 and earlier, and with subsequent versions of gcc when
> > using the -std=c++98 option, the following code fails to compile:
> > 
> > --------------------------
> > 
> > #include <iostream>
> > #include <ostream>
> > 
> > enum ByRef {by_ref};
> > 
> > template <class T>
> > class MyClass {
> >   T t;
> > public:
> >   MyClass(const T& arg): t(arg) {                  // (1)
> >     std::cout << "Without ByRef tag" << std::endl;
> >   }
> >   MyClass(T arg, ByRef): t(arg) {                  // (2)
> >     std::cout << "With ByRef tag" << std::endl;
> >   }
> > };
> > 
> > int main() {
> >   int i = 0;
> >   MyClass<int&> m2(i, by_ref);
> > }
> > 
> > --------------------------
> > 
> > gcc ignores the ByRef tag, and picks constructor 1, and then fails
> > to compile because of the resulting reference to a reference.
> > 
> 
> No, It's an error right way when you implicitly instantiate
> MyClass<int&>. Try
> 
> sizeof(MyClass<int&>);
> 
> On a literal C++03 compiler, this *must* result in a compile error
> because the declaration of the destructor is invalid. You don't even
> get to the point where a constructor could be chosen by overload
> resolution.

If I omit constructor (1), the code compiles, as does
sizeof(MyClass<int&>).

However, I think I can now see the problem: the constructor is not
templated (it can't be), so SFINAE does not apply. This means
constructor (1) must be capable of compiling for an instantiation of
MyClass<int&> even if it is never in fact called.

So gcc and comeau must, for the purposes of overload resolution, be
accepting a reference to a reference.  I assume then that Comeau in
strict c++03 mode is still accepting some non-c++03 constructs?

Chris
0
Reply Chris 12/7/2010 12:41:15 PM

On Tue, 7 Dec 2010 12:41:15 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> > Chris Vine wrote:
> > 
> > > Hi,
> > > 
> > > With gcc-4.2 and earlier, and with subsequent versions of gcc when
> > > using the -std=c++98 option, the following code fails to compile:
> > > 
> > > --------------------------
> > > 
> > > #include <iostream>
> > > #include <ostream>
> > > 
> > > enum ByRef {by_ref};
> > > 
> > > template <class T>
> > > class MyClass {
> > >   T t;
> > > public:
> > >   MyClass(const T& arg): t(arg) {                  // (1)
> > >     std::cout << "Without ByRef tag" << std::endl;
> > >   }
> > >   MyClass(T arg, ByRef): t(arg) {                  // (2)
> > >     std::cout << "With ByRef tag" << std::endl;
> > >   }
> > > };
> > > 
> > > int main() {
> > >   int i = 0;
> > >   MyClass<int&> m2(i, by_ref);
> > > }
> > > 
> > > --------------------------
> > > 
> > > gcc ignores the ByRef tag, and picks constructor 1, and then fails
> > > to compile because of the resulting reference to a reference.
> > > 

[snip]

> If I omit constructor (1), the code compiles, as does
> sizeof(MyClass<int&>).
> 
> However, I think I can now see the problem: the constructor is not
> templated (it can't be), so SFINAE does not apply. This means
> constructor (1) must be capable of compiling for an instantiation of
> MyClass<int&> even if it is never in fact called.
> 
> So gcc and comeau must, for the purposes of overload resolution, be
> accepting a reference to a reference.  I assume then that Comeau in
> strict c++03 mode is still accepting some non-c++03 constructs?

If reference to reference template arguments are not acceptable in
c++03, I think I am going to have to resort to partial template
specialisation for my purposes, which appears to behave correctly:

template <class T>
class MyClass {
  T t;
public:
  MyClass(const T& arg): t(arg) {std::cout << "Not ref type" <<
std::endl;} };

template <class T>
class MyClass<T&> {
  T t;
public:
  MyClass(T arg): t(arg) {std::cout << "Ref type" << std::endl;}
};

This is fairly grotesque.  Is anyone more knowledgeable than me able to
confirm whether strict c++03 (as well as c++98) does reject template
instantiations giving a reference to a reference, so requiring this
work around?  (Sometimes I really detest c++.)

Chris
0
Reply Chris 12/7/2010 1:16:49 PM

3 Replies
133 Views

(page loaded in 0.074 seconds)

Similiar Articles:













7/18/2012 1:42:18 PM


Reply: