1

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.

Community
  • 1
  • 1
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51

1 Answers1

1

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.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • @MooingDuck They very quietly added a bullet point to 14.7.3/1 making it legal. I'd be interested to see the proposal paper, since the feature looks hard to justify on the face of it. – Potatoswatter Jun 30 '12 at 05:31
  • The question was not C++11. I tried with MSVC 2008 after fixing syntax once again. It is saying now "error C3113: an 'enum' cannot be a template" at the point of instantiation attempt. – Kirill Kobelev Jun 30 '12 at 07:05
  • @KirillKobelev Sorry, this was a confusing coincidence. I had no idea about this C++11 feature until I looked closer at my original answer. I'll re-post it as an addition to the current answer. – Potatoswatter Jun 30 '12 at 07:27
  • @Potatoswatter, No problem. Thanks for your participation. – Kirill Kobelev Jun 30 '12 at 07:35
  • > enum type is always defined only once at its first declaration. Ha-ha. Enums perfectly allow forward declaration. – Kirill Kobelev Jun 30 '12 at 08:09
  • @KirillKobelev Sounds like a Microsoft extension. GCC appears to allow `enum blah;` *after* the definition, not before, but that is also non-conformant. – Potatoswatter Jun 30 '12 at 08:28
  • (Note, however that forward declaration of an enum is never needed, because you can always use an elaborated-type-specifier. To declare a variable of undefined `enum` type, it would be `enum blah my_blah;`. That's completely different. In C++11 an `enum` used this way must have `int` as the underlying type, IIRC.) – Potatoswatter Jun 30 '12 at 08:30
  • Forward declaration for enums IS needed because it allows using name of the enum in the named-type-specifier. This is convenient. I use this often. – Kirill Kobelev Jun 30 '12 at 08:34
  • @KirillKobelev Uh, "named-type-specifier" doesn't name a grammatical construct. Did you just make that up? Anyway, you can use `enum blah` almost anywhere you can use `blah`. Only exceptions that come to mind are functional cast notation and pseudo destructor call.. – Potatoswatter Jun 30 '12 at 08:37
  • For some time is was qurious why BNF grammar from the C++2003 standard has in the rules for enum header a `simple-or-qualifeid-id` and not simple `identifier`? Later on I found out that the follwing works fine: `class B { enum E; }; enum B::E { a,b,c, };`. Once the same works for methods and member classes, why should this be prohibited for enums? – Kirill Kobelev Jun 30 '12 at 08:38
  • @KirillKobelev Don't base your understanding of the grammar on experiments with MSVC. – Potatoswatter Jun 30 '12 at 08:39
  • @Potatoswatter, come on. I never thought like that. I think you misunderstand the role of the standard. This is a piece of paper written by people. With typos, bugs and misunderstandings. Yes, it is useful and it should be respected. But it does not come from GOD. – Kirill Kobelev Jun 30 '12 at 08:41
  • This code is actually ill-formed, no diagnostic required. The `Foo::` triggers implicit instantiation of `Foo`, which implicitly instantiates the definition of `Foo::E12`, meaning that it can't be later explicitly specialized. See the example in §14.7.3 [temp.expl.spec]/p6. – T.C. Aug 17 '14 at 19:39
  • @T.C. Almost, but not quite. See also http://stackoverflow.com/q/20110225/153285 . Implicit instantiation of `Foo` does not trigger instantiation of `E12` because it's not used by any member declaration in `Foo`. – Potatoswatter Aug 17 '14 at 20:17
  • @Potatoswatter No, implicitly instantiating a class template implicitly instantiates the definition of unscoped member enumerations (14.7.1/p1). If you look at the second half of the example in 14.7.3/p6, particularly the line marked "ill-formed, `A::E` was instantiated when `A` was instantiated", it's basically the same situation as your code. – T.C. Aug 17 '14 at 20:22
  • @T.C. Ah, unscoped enumerators! Thanks, I'll update this after I wake up. – Potatoswatter Aug 17 '14 at 21:14