15

I have the following example that I've decomposed from §14.7.3/6 [temp.expl.spec] that defines a class member enumeration in the primary template and subsequently specializes it. The following doesn't compile in clang:

template<class T>
struct A {
    enum E : T;
};

template<class T>
enum A<T>::E : T { eT };

template<>
enum A<char>::E : char { echar }; // ill-formed, A<char>::E was instantiated
                                  // when A<char> was instantiated

// error: explicit specialization of 'E' after instantiation

The reason is supposed to be that the definition of the unscoped member enumeration was instantiated before the specialization. 14.7.1 [temp.inst]/1:

The implicit instantiation of a class template specialization causes the implicit instantiation of [...] the definitions of unscoped member enumerations and member anonymous unions.

I'm trying to understand why that is a problem exactly. Is it because if the enumeration already has a definition, then that would cause a redefinition error during the specialization?

David G
  • 94,763
  • 41
  • 167
  • 253
  • 1
    In the paragraph right before the example you quoted, it says "If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required." – T.C. Aug 17 '14 at 17:47
  • 3
    This compiles for me, and provides the intended specialization. (g++ 4.8.2) @T.C. I don't really see how that would apply here. – Captain Giraffe Aug 17 '14 at 17:57
  • @T.C. I've read that several times. What would that declaration that it requires look like? – David G Aug 17 '14 at 18:07
  • @CaptainGiraffe I know. The standard doesn't require a diagnostic in that situation. I've only found clang to report an error. – David G Aug 17 '14 at 18:08
  • @CaptainGiraffe g++ should give you a warning with `-pedantic`. `A` triggers the implicit instantiaton of `A::E` using the partial specialization, so to then explicitly specialize it is ill-formed, no diagnostic required. – T.C. Aug 17 '14 at 18:34
  • @T.C. So it does. 2 in fact. `main.cpp:9:6: warning: ‘enum A::E’ is an enumeration template [-Wpedantic] enum A::E : T { eT }; ^ main.cpp:12:32: warning: template specialization of ‘enum A::E’ not allowed by ISO C++ [-Wpedantic] enum A::E : char { echar }; ` – Captain Giraffe Aug 17 '14 at 18:37
  • @0x499602D2 I think you simply can't do it if a definition of the unscoped enumeration is available for implicit instantiation. Clang is happy with a `template<> enum A::E : char;` if it's before the `template enum A::E`, but g++ is very unhappy with it. (Also, g++'s diagnostic in this area is messed up. `enum A::E : char { echar }; ` before the `template enum A::E` is OK per the standard, but g++ produces a warning with `-pedantic`.) – T.C. Aug 17 '14 at 19:00
  • 1
    Clang++ compiles this with a *scoped* enumeration. Since their definitions don't seem to be instantiated, I think this is compliant and well-formed. Either the above rule comes from C++03, where forward-declaring enums was impossibly, or this might have to do with name lookup (unscoped enum => name is injected into class scope). (The part about anonymous unions suggests to me it has to do with name lookup.) – dyp Aug 17 '14 at 21:07
  • 1
    [basic.lookup.qual]/1 "The name of a class or namespace member or enumerator can be referred to after the `::` scope resolution operator applied to a *nested-name-specifier* that denotes its class, namespace, or enumeration." So the general rule is that for `A::@`, an unscoped enumeration member can be found as a name `@`. Typically, this implies that the enumeration has to be instantiated. Possibly, you could add a special rule for name lookup during the definition of an `enum`. – dyp Aug 17 '14 at 21:24
  • You got it right: that's a redefinition error and you're doing it after the instantiation of that class. – Marco A. Sep 24 '14 at 07:46

1 Answers1

1

you need to specialize for the whole class definition:

template<class T>
struct A {
  enum E : T { eT };
};

template<>
struct A<char> {
  enum E : char { echar };
};
DU Jiaen
  • 955
  • 6
  • 14