What is a member enumeration in context of templates? - c++

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.

Related

template<> for the explicit specialization of a member enumeration

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.

Redeclared class name class-key

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.

Forward-declare a member enumeration of a class template

With C++11's strongly typed enums, it is possible to declare a member enumeration of a class like so:
class X {
public:
enum class E;
};
enum class X::E { a, b };
However, when making X a class template:
template <typename T>
class X {
public:
enum class E;
};
template <typename T>
enum class X<T>::E { a, b };
gcc 4.7.2 and clang 3.0 both complain with "error: ‘enum X::E’ is an enumeration template [-pedantic]" and "error: enumeration cannot be a template", respectively. The section of the standard I think is relevant (and which, in fact, this question originated from) is §14 Templates, the first paragraph of which states:
The declaration in a template-declaration shall
declare or define a function or a class, 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.
(emphasis mine). So is this a compiler bug, or am I mis-interpreting the statement entirely?
I have been asked for creating this answer. See paragraph [temp.mem.enum] 14.5.1.4/1 of the C++ standard:
An enumeration member of a class template may be defined outside the
class template definition. [ Example:
template<class T> struct A {
enum E : T;
};
A<int> a;
template<class T> enum A<T>::E : T { e1, e2 };
A<int>::E e = A<int>::e1;
—end example ]
Newer version of clang (3.4) compiles your code successfully with flag -pedantic-errors whereas gcc 4.8.1 still considers it is an error. I think it is a gcc bug.

Explicit specialization of non-class, non-function members of a class template

Look at the code:
template <class x> struct Foo
{
int getX(x *p) { return(0); }
enum E12 { a };
};
template <> int Foo<int>::getX(int*)
{
return(-15);
}
template <> enum Foo<int>::E12
{
a, b, c
}
As it was discussed in Cannot overload function, the first specialization is legal and even works in MSVC. While the second specialization for enum does not even want to compile, saying "error C2988: unrecognizable template declaration/definition".
It seems to me that C++ is making relaitively unlogical exception for methods. Enum is just an example. The same thing can be applied to member classes, typedefs, etc.
I will be happy is some body will comment on this.
This is a very obscure new feature of C++11. File a bug report with Microsoft, although it is unlikely it will be given priority as almost nobody is aware this is allowed. The correct syntax would be
template <class x> struct Foo
{
int getX(x *p) { return(0); }
enum E12 { a };
};
template <> int Foo<int>::getX(int*)
{
return(-15);
}
template <> enum Foo<int>::E12
{
a, b, c
};
I've filed a bug with GCC. Can someone test on recent Clang?
In C++03, only classes and functions may be explicitly specialized. From the standard, C++03 14.7.3/1:
An explicit specialization of any of the following:
function template
class template
member function of a class template
static data member of a class template
member class of a class template
member class template of a class or class template
member function template of a class or class template
can be declared by a declaration introduced by template<>
A member enum is not such a case. (Generally speaking, an enum type is always defined only once at its first declaration.)
To obtain a templated enum or typedef, you can wrap it in a class template. In your case, it would be a member class template of Foo. Such a construct is called a metafunction.
C++11 also has alias templates, which are like templated typedefs, but they cannot be explicitly specialized.
The policy of only allowing classes and functions to be specialized, and then allowing such templates to encapsulate other things like enum and typedef, seems more consistent to me than allowing direct specialization of enum. But, perhaps the language is going in your preferred direction.

Anonymous struct in typedef of trait class

Sorry for the funny title.
Prior to C++0x, there are restrictions in the use of function-local structs (“local types”) as template arguments. My question is essentially if similar restrictions apply to anonymous structs. Specifically, in the context of a trait class:
template <typename T>
struct trait;
template <>
struct trait<int> {
typedef int type;
};
template <typename T>
struct trait<std::basic_string<T> > {
typedef struct {
T value;
} type;
};
trait<std::string>::type foo; // Is this valid?
template <typename T>
void f() { }
f<trait<std::string>::type> >(); // Is this?
template <typename T>
void g() { f<typename trait<T>::type>(); }
g<std::string>(); // And this?
Is this valid and reliable? It compiles in recent versions of GCC and LLVM but I’m still insecure whether this is strictly valid, and whether it’s understood by VC++ and ICC.
For reference, the quote from the linked question in 14.3.1/2:
A local type, a type with no linkage,
an unnamed type or a type compounded
from any of these types shall not be
used as a template argument for a
template type parameter.
My interpretation is that the typedef struct is creating an alias to an unnamed type and that it thus can't be used as a template type parameter. Further note that additionally in C typedef struct {} Foo; is treated rather differently from struct Foo {}; giving precedent that the two forms are not equivalent (although admittedly that difference doesn't appear in C++).
Thus it would appear your first example works (since it's not using the unnamed type as a template type parameter), while the second and third examples would be technically invalid (since they do use it as a template type parameter).
Finally in closing I have to ask, is there a reason you can't name the struct instead of typedefing it?
EDIT: From 7.1.3/1:
...A typedef-name is thus a synonym for
another type. A typedef-name does not
introduce a new type the way a class
declaration (9.1) or enum declaration
does...
This strongly implies that using typedef in such a way does not introduce a type suitable for use as a template type-parameter.
In the upcoming standard that restriction is removed from the language. The standard says in
14.3.1 [temp.arg.type] /1
A template-argument for a template-parameter which is a type shall be a type-id.
And a typedef is a valid type-id. As a matter of fact the next paragraph contains such an example:
14.3.1 [temp.arg.type] /2
template <class T> class X { };
template <class T> void f(T t) { }
void f() {
typedef struct { } B;
B b;
X<B> x3;
f(b);
}
(Where I have trimmed most of the other examples) The example shows that an unnamed type can be used as a class template argument both in class templates and function templates.
A typedef declaration that defines an anonymous class and a typedef-name for that class, the typedef-name is the name of the class for linkage purposes. It is therefore legal to use that class as a template parameter if it meets the other criteria.
See 7.1.3p5 of the C++03 standard
If the typedef declaration defines an
unnamed class (or enum), the first
typedef-name declared by the decla-ration to be that class type (or enum
type) is used to denote the class type
(or enum type) for linkage purposes
only (3.5). [Example:
typedef struct { } *ps, S; // S is the class name for linkage purposes
This is 7.1.3p9 in the C++0x FDIS.
FWIW, this code compiles OK with MSVC2010 (modulo typos).
Well, that is equivalent to
template <typename T>
struct trait<std::basic_string<T> > {
struct type {
T value;
};
};
which is completely legitimate.