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.
Related
The C++20 standard (N4892) states:
The declaration in a template-declaration (if any) shall
[...]
(2.2) — define [...] a member enumeration, [...]
(13.1.2)
What is meant by a member enumeration in this context? I looked through the standard but could not find a definition of this term, only usages of it. In 13.9.2.3.(1/2) both scoped and unscoped member enumerations are mentioned. So I assume enums are meant by it. However, I was unable to create a member enum template in MSVC:
struct S
{
template<typename T>
enum class e
{
i = 0,
};
};
C3113: an 'enum' cannot be a template
I also have never seen an enum template in the wild, so I assume this isn't possible. So what is meant by a "member enumeration" in context of templates?
However, I was unable to create a member enum template in MSVC:
Because this was not meant. There are no enum templates in the now existing C++ standards. Think instead of a forward-declared scoped-member-enum of a class template:
template <typename T>
class Foo { enum class bar; };
template <typename T>
enum class Foo<T>::bar { baz };
This is a case of a declaration in a template-declaration which is a definition of an enum, which is an example for a use case covered by [temp.pre] 2.2.
According to 17.7.3 [temp.expl.spec] paragraph 5 (N4659),
... Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax. The same is true when defining a member of an explicitly specialized member class. However, template<> is used in defining a member of an explicitly specialized member class template that is specialized as a class template.
The explicit specialization of E definitely does not belong to the bold case, and it still needs template<>. Why is that?
template<class T> struct A {
enum E : T;
};
template<> enum A<int>::E : int { eint };
This paragraph is related to members of an explicitly specialized class template, but you have not explicitly specialized the class template. This is an example of the case it is talking about:
template<class T> struct A {
enum E : T;
};
template<> struct A<int> {
enum E : int;
};
enum A<int>::E : int { eint }; // no template<> here
In your example code, you have explicitly specialized a member of the primary template, which does require using template<>, as specified in the first paragraph.
1 An explicit specialization of any of the following:
...
(1.7) — member enumeration of a class template
...
can be declared by a declaration introduced by template<>; that is:
explicit-specialization:
template < > declaration
The underlying principle behind paragraph 5 is that once you've explicitly specialized a template, it is no longer a template, and you work with the specialization just like you would any other non-template entity.
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.
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.
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.