Разрешено ли специализировать шаблон с перечислениями с одним и тем же интегральным значением?

Посмотрите на этот простой фрагмент:

enum class Enum1 { Value };
enum class Enum2 { Value };
template <auto> struct Foo;
template <> struct Foo<Enum1::Value> { };
template <> struct Foo<Enum2::Value> { };

Clang компилирует это, но gcc-7.2 не работает:

x.cpp: 5: 20: ошибка: переопределение шаблона структуры Foo < (Enum1) 0 > < > struct Foo {};

Это сообщение об ошибке кажется недействительным, как указано в строке 5, Enum2::Value.

Какой компилятор прав? Соответствует ли этот код?

Ответы

Ответ 1

В [dcl.type.auto.deduct]:

Тип T, содержащий тип заполнителя и соответствующий инициализатор e, определяется следующим образом:

  • для параметра шаблона непигового типа, объявленного типом, который содержит тип заполнителя, T является объявленным типом параметра непигового шаблона, а e является соответствующим аргументом шаблона.

Это, по-видимому, предполагает, что выведенный тип будет decltype(Enum1::Value), а значение будет Enum1::Value.


Является ли decltype(Enum1::Value) равным decltype(Enum2::Value)? Этот код...

static_assert(std::is_same_v<decltype(Enum1::Value), decltype(Enum2::Value)>);

... не удается скомпилировать как с clang++ 6, так и g++ 8.


Я думаю, что вы, возможно, обнаружили ошибку в gcc. Как отметил Йоханнес Шауб в комментариях, там также в пользу gcc-поведения.

Откроется отчет об ошибке: # 79092.


Также обратите внимание, что следующий код принят обоими компиляторами:

template <typename T, T> struct Foo;
template <> struct Foo<decltype(Enum1::Value), Enum1::Value> { };
template <> struct Foo<decltype(Enum2::Value), Enum2::Value> { };

template <auto> ведет себя иначе, чем это было бы (IMHO), удивляющее и нежелательное.