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> { /*..*/ };
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.
The following code is accepted by VC++ 2013, but rejected by clang 3.4.
Which compiler is right as per the C++ standard?
template<class T>
struct A
{
T n;
};
template<class T>
struct B : A<T>
{
// VC++ 2013 : OK
// clang : error : use of class template 'A' requires template arguments
B& f1(const A& obj)
{
return *this;
}
// VC++ : OK
// clang : OK
B& f2(const A<T>& obj)
{
return *this;
}
};
int main()
{
B<int> b;
}
My first instinct is to say VC++ is right in this one. Lookup of the name A in B should find the injected-class-name A inside A<T>, which can also be used as a type-name to refer to A<T>.
C++11 [temp.local]:
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 <>.
2 ...
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>
};
However, at the same time, [temp.dep]§3 states:
3 In the definition of a class or class template, if a base class depends on a template-parameter, the base class
scope is not examined during unqualified name lookup either at the point of definition of the class template
or member or during an instantiation of the class template or member.
Based on this, I'd more inclined to say that clang is actually right, since the injected-class-name A is inside the scope of A<T>, which depends on B's template parameter T and is thus not searched during unqualified name lookup. Secondary evidence to support this would be the fact that the example from [temp.local] uses Derived::Base instead of just Base.
So in total, I'd say that
it's a nice corner-case, and
clang is actually right not to examine the scope of A<T>
Clang is correct; although the injected-class-name of the class template A is certainly visible within A<T> and so visible within the derived class B<T>, it is a dependent name and so the base class scope is not examined within B<T>.
Dependent name base class scope lookup is discussed in 14.6.2p3:
In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
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 >
// ^^
Here's the scenario:
template <template <typename> class T, typename V>
struct parent {
void do_something();
};
template <typename V>
struct child : public parent<child, V> {
void do_something(V argument);
using parent<child, V>::do_something; // C3200: invalid template argument for template parameter 'IMPL', expected a class template
};
The above code fails to compile on the given line with the given error (MSVC 9.0). However if I write this instead, outside of the class definition for child:
template <typename V>
struct parent_identity_meta {
typedef typename parent<child, V> type; // no error!
};
I can now successfully do the following, within child:
using parent_identity_meta<V>::type::do_something;
I know there's a limitation (alleviated in C++11) that you can't typedef against a template, but I don't think that's what I'm running into here, otherwise the typedef in parent_identity_meta would fail. It seems like child refers to the template when not inside of its own class definition, and to the class being generated from within itself.
This is pretty understandable (having to write child<V> every single time would be painful); but is there any way to override this behaviour?
This is a place where C++03 and C++11 are different from each other. The relevant part of the standard is [temp.local]/1. In C++03, this states:
Like normal (non-template) classes, class templates have an injected-class-name (clause 9). The injected-class-name can be used with or without a template-argument-list. When it is used without a template- argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.
This means that child (without any template arguments) refers to the specialization child<V>. In C++11, it was changed to:
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 <>.
Note in particular When it is used ... as a template-argument for a template template-parameter ... it refers to the class template itself.. This means that in C++11, your code would be correct.
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!