c++structenumsclang

Dynamic initialization of enum class inside template struct


I was compiling following test code with clang :

template<typename T> struct S1
{
   struct S2{
       enum class E1;
       enum class E2: T;
       enum class E3: short;
    };

    typename S2::E1 b1;
    typename S2::E2 b2;
    typename S2::E3 b3;

    enum class S1::S2::E1 {e11,e12};
    enum class S1::S2::E2 : T {e21,e22};
    enum class S1::S2::E3 : short {e31,e32};
};

template struct S1<int>;

I got error : template specialization or definition requires a template parameter list corresponding to the nested type 'S1< T >'. My guess is since struct S1 is being defined while adding member in

enum class S1::S2::E1 {e11,e12} 
enum class S1::S2::E2 : T {e21,e22};
enum class S1::S2::E3 : short {e31,e32};

the compiler does not know what is T since S1 is not instantiated yet and hence T cannot be resolved. The compiler thus does not know the size for the enum members and hence throws error. Is this correct? Is it specified in standards?

Note : gcc does not throw any such error.


Solution

  • It's simply not allowed to define an enum like that. Paragraph 7.2/4 in n3337 states:

    If the enum-key is followed by a nested-name-specifier, the enum-specifier shall refer to an enumeration that was previously declared directly in the class or namespace to which the nested-name-specifier refers (i.e., neither inherited nor introduced by a using-declaration), and the enum-specifier shall appear in a namespace enclosing the previous declaration.

    Granted, the error message is not exactly stellar. Your example can be vastly simplified to this:

    template<typename T> struct S1
    {
       struct S2 {
           enum class E;
       };
    
       enum class S2::E {};
    };
    

    which will produce the same error message.

    Valid options are:

    // definition in the scope that the declaration appears in
    template<typename T> struct S1
    {
       struct S2 {
           enum class E;
           enum class E {};
       };
    };
    

    or

    // definition in the enclosing namespace scope
    template<typename T> struct S1
    {
       struct S2 {
           enum class E;
       };
    };
    
    template<typename T>
    enum class S1<T>::S2::E {};
    

    The same rule applies also for nested classes (see 9.7/3). If you try this

    template<typename T>
    struct S1
    {
       struct S2 {
           struct S3;
       };
    
       struct S2::S3 {};
    };
    

    then GCC, too, produces a (similarly unhelpful) error. That it doesn't do the same with enums appears to be a bug.

    And your own explanation you brought up for the error is incorrect. In the definition of a template the compiler doesn't need to (and obviously can't) know what T is. It only needs it when it instantiates the template. How else would something like template<typename T> struct X { T obj; }; work?