Is it possible to explicitly instantiate a template class through a template alias?
If so, how? Otherwise, can someone point to the ISO paper in which this was discussed and decided against?
template<class T>
struct A { };
/// Explicit instantiate A for int:
template struct A<int>;
/// Alias
template<class T>
using B = A<T>;
/// Explicitly instantiate A for double via alias B:
template struct B<double>;
/// error: elaborated type refers to a non-tag type
Shouldn't this instantiate A<double> since B<T> is just a different name for A<T> ?
This is indirectly forbidden, because:
7/3 forbids writing the explicit specialization without a class-key (class, struct, or union):
In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 9) or enumeration (7.2), that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key (9.1), or an enum-specifier.
7.1.6.3/2 forbids combining a class-key with an alias template specialization:
3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. ... If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.
Related
(This question is not about template template arguments.)
I just discovered that GCC compiles such code
template <typename A, typename B>
struct P {};
template <typename A>
template <typename B>
using Q = P<A, B>;
where Q is a doubly-templated name.
But I can't use this. When I write Q<short><long>, I get
template_template.cpp:10:5: error: ‘Q<short int>’ is not a template
Q<short><long>{};
^~~~~~~~
template_template.cpp:10:20: error: invalid use of incomplete type ‘Q<short int>’
Q<short><long>{};
^
template_template.cpp:2:8: note: declaration of ‘Q<short int>’
struct P {};
Why is the first snippet compiled?
Is there a syntax to convince the compiler that Q<short> is actually a template?
// GCC 6.3.0
The C++14 standard says in 14p1:
The declaration in a template-declaration shall
— declare or define a function, a class, or a variable, or
— define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
— define a member template of a class or class template, or
— be an alias-declaration
Here the declaration within the template-declaration is none of the above (it is another template-declaration, which itself contains an alias-declaration), and therefore the code is invalid.
The relevant parts of the grammar are:
template-declaration:
template <template-parameter-list> declaration
alias-declaration:
using identifier attribute-specifier-seqopt = type-id ;
where a declaration can be a template-declaration, an alias-declaration, or other types of declarations.
Note that the grammar itself accepts the given code, but the additional restrictions in the text above make it invalid.
I've seen the following pre-C++11 code, used as a trick to declare class template friends (which in C++11 can simply be done with friend T;)
template <typename T>
struct Wrapper
{
typedef T type;
};
template <typename T>
class Foo
{
friend class Wrapper<T>::type; // effectively makes T a friend
};
struct Test{};
int main()
{
Foo<Test> foo;
}
The code compiles fine on g++ (4.9/5.1/6), but fails under clang++ (3.5/3.6/3.7) with the error
error: elaborated type refers to a typedef
friend class Wrapper::type;
Is the code above standard compliant, i.e. valid or not?
§7.1.6.3/2:
If the identifier resolves to a typedef-name or the
simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.
It's not compliant. The grammar rules for friend in [class.friend]/3 are:
A friend declaration that does not declare a function shall have one of the following forms:
friend elaborated-type-specifier ;
friend simple-type-specifier ;
friend typename-specifier ;
class Wrapper<T>::type is none of those specifier types. It's not an elaborated-type-specifier because Wrapper<T>::type isn't an identifier or a class-name, and obviously isn't one of the other two either. What you're looking for is simply:
friend typename Wrapper<T>::type;
[dcl.typedef]/p8:
[ Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (9.1)
If a typedef-name is used to identify the subject of an elaborated-type-specifier (7.1.6.3), a class definition
(Clause 9), a constructor declaration (12.1), or a destructor declaration (12.4), the program is ill-formed.
— end note ] [Example:
struct S {
S();
~S();
};
typedef struct S T;
S a = T(); // OK
struct T * p; // error
— end example ]
The code should fail at template instantiation time, which it does so correctly in Clang.
Using typename in place of struct allows the code to pass in both compilers.
The following quote is from 14.5.1/4 [temp.class]:
In a redeclaration, partial specialization, explicit specialization or
explicit instantiation of a class template, the class-key shall agree
in kind with the original class template declaration
I thought it meant that we cannot declare an explicit specialization with another class key, for instance:
template <class T, class W>
struct A
{
void foo();
};
template <class T, class W>
class A<T*, W> // Should have printed an error
{
void foo();
};
DEMO
But it works fine. So what's the point of that rule?
Right after the quoted sentence is a reference to [dcl.type.elab]. [dcl.type.elab]/p3 describes what "agree in kind" means:
The class-key or enum keyword present in the
elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. [...]
Thus, in any elaborated-type-specifier, the enum keyword shall be
used to refer to an enumeration (7.2), the union class-key shall
be used to refer to a union (Clause 9), and either the class or
struct class-key shall be used to refer to a class (Clause 9)
declared using the class or struct class-key.
In other words, if the primary template is a union, the "redeclaration, partial specialization, explicit specialization or explicit instantiation" must use union; otherwise it can use either class or struct, but not union.
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.
C++ Standard
Section 14/2 :
In a function template declaration,
the declarator-id shall be a
template-name (i.e., not a
template-id). [Note: in a class
template declaration, if the
declarator-id is a template-id, the
declaration declares a class
template partial specialization.
What is the difference between a template-name, template-id and a type-id?
Does the above quote mean we cannot write something like
template <>
void templatefunction<int>(){ // ...}
or have I misunderstood the point?
The template-name is the name of the template. In your example, templatefunction is a template-name.
The template-id is the name of the template with the template arguments list. In your example, templatefunction<int> is the template-id. A template-id names a template specialization.
A type-id names a type. A template-id is a type-id; a template-name is not (because it does not name a type; it names a template).
The text you cite from 14/2 concerns a template-declaration, which declares a primary template. Your example is not a template-declaration, it is an explicit-specialization (14.7.3/1).
A declarator-id is the syntactical element that specifies the name in a simple-declaration ("type name;"). In the following "A" and "B::C" is the declarator-id
int A;
int B::C;
int A();
int *A;
int A[42];
template<typename T> void A();
A type-id syntactically is roughly a simple-declaration where the declarator-id is missing. A type-id is used as the syntactical element in a template type argument and in a cast.
int // type-id
int* // type-id
int[] // type-id
int() // type-id
int(*)() // type-id
A template-name is the name of a template. Syntactically it appears before a template-argument list. The above quote misuses "template-name" and "declarator-id", because a template-name is a plain identifier and does not contain any qualifiers. C++0x has changed the text to
In a function template declaration, the last component of the declarator-id shall be a template-name or operator-function-id (i.e., not a template-id).
(The last part appears in cases such as operator+()). Even the C++0x text misses some cases - see this defect report.
The misuse of "declarator-id" happens in the note. The note was replaced by C++0x with
[ Note: in a class template declaration, if the class name is a ... — end note ]
In class template declarations, the name specified syntactically is a class-name instead of a declarator-id. The relation of class-name and declarator-id is as follows (very simplified...)
class class-name { ... } declarator-id;
class foo { ... } bar;
In class template declarations, there may not be a declarator-id specified.
A template-id is a template-name followed by a template-argument list.
The quote means that in a function template declaration, the name must not be a template-id. In your example you declare a function instead of a template. There are still cases where an explicit specialization declares a template, though. But that can only happen for member function templates
template<typename T>
struct A {
template<typename U>
void f();
};
// this explicit specialization *contains* a template declaration and
// declares an identifier (a template-name) qualified by A<int>::
template<> template<typename U>
void A<int>::f() { }
From C++ Templates: The Complete Guide By David Vandevoorde, Nicolai M. Josuttis
8.3
Explicit template arguments: A template name can be followed by explicit template argument values enclosed in angle brackets. The resulting name is called a template-id.
For example:
template <typename T>
struct Demo{
// ...
};
int main()
{
Demo <int> d; // Demo is the template name, Demo<int> is the template-id
// ...
}
In a function template declaration, the declarator-id shall be a template-name (i.e., not a template-id).
For example (from what I have understood):
class A {
public:
template <typename T> void f(T);
template <typename T> struct X { };
};
class B : public A {
public:
using A::f; // fine
using A::X // fine
};
class C : public A {
public:
using A::f<int>; // ill formed, declarator-id shall not be a template id
using A::X<double> // ill formed, declarator-id shall not be a template id
};
Someone please correct me if I am wrong.