Forward-declaration of enum member of template-specialization - fails with GCC
Asked Answered
D

1

9

I know that, generally, we can forward-declare enums in C++11.

So, why does this:

enum kind_t { kind1, kind2 };

template <kind_t Kind> struct foo {};

template <> struct foo<kind1> {
    enum named : int;
};

enum foo<kind1>::named : int {
    named1 = 123,
    named2 = 456,
};

fail to compile with GCC (12.1)? The error is (Godbolt):

<source>:9:6: error: cannot add an enumerator list to a template instantiation
    9 | enum foo<kind1>::named : int {
      |      ^~~~~~~~~~
ASM generation compiler returned: 1
<source>:9:6: error: cannot add an enumerator list to a template instantiation
    9 | enum foo<kind1>::named : int {
      |      ^~~~~~~~~~

This seems to compile fine with clang++ 14.0...

Dobbs answered 24/8, 2022 at 21:22 Comment(8)
It's interesting, that if you mark the definition with template<> it becomes invalid for clang, but valid for gcc - godbolt.org/z/GGjjcnPMxMenagerie
@TheDreamsWind: That's right... my IDE was complaining about the template<>, but actually it seems like it should be there... or should it?Dobbs
temp.mem.enum is the closest part of standard i've managed to find so far, but it has nothing about specialisation/instantiation restrictionsMenagerie
The above code compile with Visual Studio. However the fact that not compiler agree might indicate an area that is not yet well defined... and probably not very used. Do you any reason of mixing template and forward declared enumerations?Scarrow
See CWG 1485 Out-of-class definition of member unscoped opaque enumeration.Splanchnic
@Phil1970: Yes. I declare the template in a more general header, along with other stuff in struct foo (it's basically a traits class) and define the enum values in separate headers that are related to the specific instantiation.Dobbs
Then, why not put the template specialization in the same header where the enum is defined? Alternatively, why not defining top level enum and put have something like enum kind1_named; template <> struct foo<kind1> { using named = kind1_named; };Scarrow
@Phil1970: Because the specialization has very few requirements, while all sorts of declarations depend on it.Dobbs
D
0

The forward-declare enums in your currently code encounters error while clang and msvc seems to handle it fine.

enum kind_t { kind1, kind2 };

template <kind_t Kind> struct foo {};

template <> struct foo<kind1> {
    enum named : int;
};

enum foo<kind1>::named : int {
    named1 = 123,
    named2 = 456,
};

Try to define the enum directly inside the class specialization without a forward declaration if you want to make your code compatible with GCC (12.1):

enum kind_t { kind1, kind2 };

template <kind_t Kind> struct foo {};

template <> struct foo<kind1> {
    enum named : int {
        named1 = 123,
        named2 = 456,
    };
};

Live Demo

Dealfish answered 13/9, 2024 at 5:49 Comment(1)
So, you're saying it's a compiler bug?Dobbs

© 2022 - 2025 — McMap. All rights reserved.