Template Base Constructor Call in Member Initialization List Error

Template base constructor call in member initialization list error

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() {};

How to initialize members of template-base class from derived constructor initializer list?

You need to initialize the base class in the member initialization list of the derived class. Since your base doesn't have a constructor you can use curly brace initialization (uniform initialization) like

template <typename T>
struct base {
T a;
};

template <typename T>
struct derived : base<T> {
derived(T v) : base<T>{v} {}
};

Compiler Error When Calling Base Constructor when both Base and Derived are Templated With Derived Type Parameter

The injected class name Base is a member of the class Base, and since the base class is dependent, its scope is not searched during unqualified name lookup. As such, using the name Base will only find the class template, not the injected class name of Base<T>. That is why you must write Base<T>.

Derived::Base works because it causes name lookup to be postponed until Derived is instantiated.

Initializing template base-class member types in derived-class initializer lists

The Foo_T type will not be looked up in the base class when used in the derived (Bar) constructor.

Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<T>(bar_arg) // base-class initializer
{
Foo_T = TypeA(a_arg); TypeA, etc. // Won't compile, per the standard
}

This is per the C++ standard, which says unqualified names are generally non-dependent, and should be looked up when the template is fully defined.

Since a template base class definition is not known at that time (there could be fully specialised instances of the template being pulled in later in the compilation unit), unqualified names are never resolved to names in dependent base classes.

If you need a name from a base class when templates are involved, you have to either fully qualify them, or make them implicitly dependent in your derived class.

 Foo< T >::Foo_T = TypeA(a_arg);   // fully qualified will compile

or, make it dependent

 this->Foo_T = TypeA(a_arg);

Since the this makes it template dependent, resolving the type is postponed till "phase 2" of template instantiation (and then, the base class is also fully known)

Note that if you wanted to use a function from the base class, you could have also added a using declaration..

(inside Bar())

  some_foo_func(); // wouldn't work either

using Foo<T>::some_foo_func;
some_foo_func(); // would work however

Why can't Initialize the data member of base class in the constructor initializer list of derived class?

By the time the constructor of the derived class is invoked, the base class must already be constructed. So it's already too late. To see why it must be this way, consider:

class Base
{
public:
int i;
Base(int q) : i(q) { ; }
};

class Middle : public Base
{
public:
Middle() : Base(2) { printf("i=%d\n", i); }
};

class Derived : public Middle
{
public:
Derived() : i(3) { ; }
}

Now, think about it. The Middle constructor has to run before the Derived constructor. And the Middle constructor ensures that i is 2. So how can the Derived constructor later re-construct it with a different value?



Related Topics



Leave a reply



Submit