Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template base constructor call in member initialization list error

I have a base class that looks like the following:

template<typename T>
class Base
{
   public:
      Base(int someValue);

      virtual T someFunc() =0;
};

template<typename T>
Base<T>::Base(int someValue)
{}

And then the following:

#include "base.hpp"

class Foo
   : public Base<Foo>
{
   public:
      Foo(int someValue);

      virtual Foo someFunc();
};

Foo::Foo(int someValue)
   : Base(someValue)
{}

I get the following error from gcc 4.2.1.

error: class ‘Foo’ does not have any field named ‘Base’

I should mention this compiles fine on my Fedora box which is running gcc 4.6.2. This error occurs when compiling on my os x Lion machine.

EDIT

Problem seems to be that I am not indicating type of template in the Foo class when calling the constructor. The following fixes the error in os x.

: Base<Foo>(someValue, parent)

EDIT

Yes this does look like a bug. What I mentioned before fixes the error under os x and code compiles fine in fedora with that fix. Will go and see if there is an update to gcc in os x.

like image 662
user174084 Avatar asked Jan 16 '12 23:01

user174084


1 Answers

First:

[C++11: 12.6.2/3]: A mem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.

[ Example:

struct A { A(); };
typedef A global_A;
struct B { };
struct C: public A, public B { C(); };
C::C(): global_A() { } // mem-initializer for base A

—end example ]

And Base should be a valid injected-class-name for the base here (that is, you can use it in place of Base<T>):

[C++11: 14.6.1/1]: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

[C++11: 14.6.1/3]: The injected-class-name of a class template or class template specialization can be used either as a template-name or a type-name wherever it is in scope. [ Example:

template <class T> struct Base {
   Base* p;
};

template <class T> struct Derived: public Base<T> {
   typename Derived::Base* p; // meaning Derived::Base<T>
};

template<class T, template<class> class U = T::template Base> struct Third { };
Third<Base<int> > t; // OK: default argument uses injected-class-name as a template

—end example ]

I haven't found anything to indicate that this doesn't apply in the ctor-initializer, so I'd say that this is a compiler bug.

My stripped-down testcase fails in GCC 4.1.2 and GCC 4.3.4 but succeeds in GCC 4.5.1 (C++11 mode). It seems to be resolved by GCC bug 189; in the GCC 4.5 release notes:

G++ now implements DR 176. Previously G++ did not support using the injected-class-name of a template base class as a type name, and lookup of the name found the declaration of the template in the enclosing scope. Now lookup of the name finds the injected-class-name, which can be used either as a type or as a template, depending on whether or not the name is followed by a template argument list. As a result of this change, some code that was previously accepted may be ill-formed because

  • The injected-class-name is not accessible because it's from a private base, or
  • The injected-class-name cannot be used as an argument for a template template parameter.

In either of these cases, the code can be fixed by adding a nested-name-specifier to explicitly name the template. The first can be worked around with -fno-access-control; the second is only rejected with -pedantic.


My stripped-down testcase with Qt abstracted out:

template <typename T>
struct Base { };

struct Derived : Base<Derived> { // I love the smell of CRTP in the morning
   Derived();
};

Derived::Derived() : Base() {};
like image 180
Lightness Races in Orbit Avatar answered Oct 08 '22 19:10

Lightness Races in Orbit