Template base constructor call in member initialization list error

12,524

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() {};
Share:
12,524

Related videos on Youtube

user174084
Author by

user174084

Updated on June 04, 2022

Comments

  • user174084
    user174084 almost 2 years

    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.

  • Lightness Races in Orbit
    Lightness Races in Orbit over 12 years
    @Neox: Why? Why is the base's injected-class-name not sufficient?