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.
Related
Consider an example:
#include <type_traits>
#include <string>
template <template <class> class TT> //#1
struct Foo {
static void foo() {
static_assert(std::is_same_v<decltype(TT("abc")), TT<std::string>>);
}
};
template <class T>
struct Bar {
Bar(T) {}
};
template <class T>
Bar(T) -> Bar<std::string>; //#2
int main() {
Foo<Bar>::foo();
}
[clang] as well as [gcc] both seem to use user provided deduction guides (#2) when deducing the template parameter of template template parameter (#1). Is it a standard compliant feature?
Yes, this is standard compliant.
According to [dcl.type.simple]/2:
A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type ([dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name.
And [temp.param]/3:
A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared without template) or template-name (if declared with template) in the scope of the template declaration.
TT is a type-parameter declared with template, which makes it a template-name and hence a placeholder for a deduced class type. All the usual rules apply just fine.
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 code compiles using both Clang and GCC, even though Foo_t<T>::Bar doesn't have typename in front of it:
struct Foo {
using Bar = int;
};
template<class...>
using Foo_t = Foo;
template<class T>
void f(){
Foo_t<T>::Bar b; // No typename!
}
int main(){
f<int>();
}
Should it compile?
Introduction
Foo_t<T>::Bar might look like a dependent-name, but it isn't since the template-arguments passed to the alias-declaration are not used when determining what the qualified-id Bar is referring to.
The code is well-formed.
What does the Standard (N3337) say?
14.5.7/2 Alias templates [temp.alias]
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type
obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias
template.
A.6 Declarations [gram.dcl]
alias-declaration:
using identifier attribute-specifier-seq_opt = type-id ;
What is the Standard really saying?
Since there are no template-parameters in the type-id of Foo_t, the template alias-declaration is always directly equivalent to Foo, no matter what template-arguments we pass to it.
template<class... Ts>
using Foo_t = Foo;
// ^--- "Foo" = type-id
Replacing the usage of Foo_t<T> with the equivalence of the template alias-declaration leaves us with the following:
template<class T>
void f(){
Foo::Bar b; // ok, nothing here depends on `T`
}
With some more digging, this is CWG issue 1390.
The issue description is
According to 14.6.2.1 [temp.dep.type] paragraph 8, a type is dependent
(among other things) if it is
a simple-template-id in which either the template name is a template
parameter or any of the template arguments is a dependent type or an
expression that is type-dependent or value-dependent
This applies to alias template specializations, even if the resulting
type does not depend on the template argument:
struct B { typedef int type; };
template<typename> using foo = B;
template<typename T> void f() {
foo<T>::type * x; //error: typename required
}
Is a change to the rules for cases like this warranted?
And there's a note in that issue:
Notes from the October, 2012 meeting:
CWG agreed that no typename should be required in this case. In some
ways, an alias template specialization is like the current
instantiation and can be known at template definition time.
The issue is still in "drafting" status, but it looks like the compiler vendors are already implementing the intended resolution.
§14/2 (emphasis mine)
A template-declaration can appear only as a namespace scope or class
scope declaration. In a function template declaration, the last
component of the declarator-id shall not be a template-id. [ Note:
That last component may be an identifier, an operator-function-id,
a conversion-function-id, or a literal-operator-id. In a class
template declaration, if the class name is a simple-template-id, the
declaration declares a class template partial specialization (14.5.5).
—end note ]
Note that declarator-id is defined in the grammar (N4140) as shown below:
declarator-id:
...opt id-expression
id-expression:
unqualified-id
qualified-id
unqualified-id:
identifier
operator-function-id
conversion-function-id
literal-operator-id
~ class-name
~ decltype-specifier
template-id
Given that, in a function template declaration, a declarator-id is an identifier, an operator-function-id, a conversion-function-id, or a literal-operator-id, what does it mean the expression "last component" above?
In your quote from the grammar, you have omitted the production for qualified-id which expands to a sequence of unqualified-ids, and last component refers to the last of those unqualified-ids.
The Standard is telling you that
a function template declaration cannot end in a template-id
in contrast to a partial specialisation of a class template
template<typename T> class A {};
template<typename T> class A<std::vector<T> > {}; // ends with simple-template-id
even though some components of a function template's qualified-id could be template-ids, as in
template<typename>
struct A {
void f();
};
template<typename T>
void A<T>::f() {} // cannot end with template-id
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.