I'm getting compilation errors when trying to call the base class constructor in derived initialization list when using a template template parameter with CRTP.
Problem can be replicated with this snippet of code:
template <template<class> class Derived, class T>
struct base
{
};
template <class T>
struct derived : public base<derived, T>
{
derived()
: base<derived, T>()
{ }
};
The offending error messsage:
bug.cpp:10:16: error: template argument for template template parameter must be a class template or type alias template
: base<derived, T>()
^
bug.cpp:10:11: error: expected class member or base class name
: base<derived, T>()
^
bug.cpp:10:11: error: expected '{' or ','
3 errors generated.
This problem only appears to happen on clang (3.4), not g++ (4.8, 4.7, 4.6). I'm compiling with -std=c++11 also.
This is the first time I've needed to use CRTP with template template parameter. Am I doing this okay and it's a problem with clang++ or not?
I've grown to trust clang++ error messages more than g++ of late!
Your code is legal.
From the C++11 Standard, section 14.6.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.
Looks like your version of clang is still implementing the old rule. Based on your additional comments, it might be doing so only in the ctor-initializer-list.
User David Rodríguez - dribeas provided a workaround for compilers that haven't fully implemented the C++11 injected-class-name rule. Use any name of the class that isn't unqualified, for example:
derived()
: base< ::derived, T >()
// ^^ qualified with global namespace
{ }
Some compilers may require this in the inheritance list also:
template <class T>
struct derived : public base< ::derived, T >
// ^^
Related
The following code is not valid:
struct base {
};
struct inherit : const base {
};
You cannot inherit from a const type.
Does the situation change when templates are involved? In other words, is this code valid:
struct base {
};
template<typename T>
struct inherit : T {
using T::T;
};
int main() {
inherit<base const>{};
}
gcc is says it is fine, but clang reports
<source>:6:2: error: 'const base' is not a direct base of 'inherit<const base>', cannot inherit constructors
using T::T;
^ ~
<source>:10:2: note: in instantiation of template class 'inherit<const base>' requested here
inherit<base const>{};
^
1 error generated.
Compiler returned: 1
To make clang happy, I need to do something like this:
template<typename T>
struct inherit : T {
using U = std::remove_const_t<T>;
using U::U;
};
Which version is correct? Or are neither of them correct and I need to inherit from std::remove_const_t<T>?
Thanks to #T.C. we have:
According to [temp.param]/3:
A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared with class or typename) ... in the scope of the template declaration.
So it works just like a typedef.
And then [class.name]/5:
If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored.
Hence GCC is right, const should be stripped when inheriting from T, since a class-name is required at that point, as well as in the using T::T; inheriting constructors declaration.
I am trying to forward a template parameter to a parent class (from a subclass), however I am getting the following error when trying to do so;
test.cc:9:46: error: type/value mismatch at argument 1 in template parameter list for ‘template<class A, class B> class BaseClass’
class SubClass : public BaseClass<SubClass, B> {
^
test.cc:9:46: error: expected a type, got ‘SubClass’
Here is the code I am trying to run;
#include <cstdlib>
template<typename A, typename B>
class BaseClass {
};
template<typename B>
class SubClass : public BaseClass<SubClass, B> { // Trying to forward B to the second parameter
};
int main(void) {
return EXIT_SUCCESS;
}
I have tried prefixing B with typename, but I get the following error instead;
test.cc:9:55: error: type/value mismatch at argument 1 in template parameter list for ‘template<class A, class B> class BaseClass’
class SubClass : public BaseClass<SubClass, typename B> {
^
test.cc:9:55: error: expected a type, got ‘SubClass’
test.cc:9:55: error: template argument 2 is invalid
template<typename B>
class SubClass : public BaseClass<SubClass<B>, B>
// ^^^
{
};
SubClass is an injected name that can be used to refer to SubClass<B> only in the scope of SubClass. Outside, it is the name of a class template itself (that it, it requires a template parameter list <> to form a type).
§ 9 [class]/p2:
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
§ 3.3.2 [basic.scope.pdecl]/p8:
The point of declaration for an injected-class-name (Clause 9) is immediately following the opening brace of
the class definition.
§ 14.6.1 [temp.local]/p1:
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 <>.
You're trying to use the injected-class-name. However,
The class-name is also inserted into the scope of the class itself;
this is known as the injected-class-name.
The base-clause is not looking up names that are within the scope of the class. Hence you have to supply the argument list yourself.
template<typename B>
class SubClass : public BaseClass<SubClass<B>, B> { /*..*/ };
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.
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() {};
This code:
template <class T>
class Foo {};
typedef Foo<void*> Bar;
template <class T>
class Foo<T*> : public Bar {};
// use Foo<int*> somewhere.
Compiles and works fine in MSVC 9.0, but doesn't compile in GCC 4.1.1 or GCC 4.3.4, with the error:
error: invalid use of undefined type 'class Bar'
Is this illegal C++ that MSVC accepts incorrectly, or a limitation of GCC?
Either way, how can I work around this get the desired behaviour: pointer specialisations of Foo that inherit from unspecialised Foo<void*>?
You cannot do that, except by writing the specialization for all T*, except when T is void. Otherwise, you will derive the class from itself, which for obvious reasons can't work.
Instantiating the primary class template for arguments that have an explicit or partial specialization is not possible. If you try to, by provoking an instantiation before the explicit or partial specialization is visible (note that your code did not provoke such an instantiation), your program is ill-formed, with no diagnostic being required (which effectively renders the behavior undefined).
To achieve the above work-around, you can use SFINAE
template <class T>
struct isnt_void { typedef void type; };
template<> struct isnt_void<void> { };
template <class T, class = void>
class Foo {};
template <class T>
class Foo<T*, typename isnt_void<T>::type> : public Foo<void*> {};
The typedef is a red herring.
The following code is equivalent:
template <class T>
class Foo {};
template <class T>
class Foo<T*> : public Foo<void*> {};
It should be clear that, although Foo<T*> is declared at this point, it is not defined. And thus you may not use it as a base.
[class.name] (2003 wording, 9.1/2):
A class definition introduces the class name into the scope where it is defined
[class.mem] (2003 wording, 9.2/2):
A class is considered a
completely-defined object type (3.9)
(or complete type) at the closing } of
the class-specifier. Within the class
member-specification, the class is
regarded as complete within function
bodies, default arguments and
constructor ctor-initializers
(including such things in nested
classes). Otherwise it is regarded as
incomplete within its own class
member-specification.
[class.derived] (2003 wording, 10/1):
The class-name in a base-specifier shall not be an incompletely defined class (clause 9);
A superior solution would be to compose of Foo<void*>. After all, you don't want the raw void* interface cluttering up your stuff, and you don't want a Foo<T*> to be convertible to a Foo<void*>.
Alternatively, you could fully specialize Foo<void*> beforehand.
Assuming, of course, that you're doing this for type folding, instead of because you actually want inheritance.
I have this simple code:
template<template <class> class Generator>
class TestHelper {};
template<class Writer>
class Test
{
typedef TestHelper< Test > Helper;
};
It's works great on last g++ versions, but, in 4.4 or 4.5, I get this error:
test.cpp:7: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class> class Generator> class TestHelper'
test.cpp:7: error: expected a class template, got 'Test<Writer>'
What I'm doing wrong?
It's because inside the body of class Test<Writer>, naming Test without providing the template arguments automatically assumes the same arguments (e.g. Writer).
For example, this allows you to write the copy constructor as:
Test(const Test&);
instead of
Test::Test(const Test<Writer>&);
You can overcome this by qualifying Test with its namespace, e.g.
typedef TestHelper< ::Test > Helper;
NOTE: As Tomalek suggests, the original usage is valid in C++0x. Here is the relevant paragraph of the standard (emphasis mine), from section 14.6.1 ([temp.local]):
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 <>.
#Ben is probably right, but here's a totally different way to get it to compile, that doesn't use templates as args to templates.
template<class Generator> // changed this to a simpler template
class TestHelper {};
template<class Writer>
class Test
{
typedef TestHelper< typename Test :: Writer > Helper; // 2nd change
};
I made two changes. #Hugo, maybe this is what you wanted, and maybe this is what older versions of g++ did?
It's easy to get code to compile, but that doesn't mean that it does what you think it does!