несоответствие clang / gcc в специализации классов

9

Я столкнулся с этой проблемой, пытаясь специализировать tuple_size/ tuple_elementдля пользовательского класса в C ++ 17 для структурированного связывания.

Ниже код компилируется в GCC, но не в clang (обе версии транка, см. Ссылку ниже).

#include <type_traits>

template<typename T, typename... Ts>
using sfinae_t = T;

template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;

template <typename T>
struct Test;

template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};

void f() {
    Test<int> t;
}

https://godbolt.org/z/ztuRSq

Это ошибка, предоставленная clang:

<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};

       ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1 error generated.

Compiler returned: 1

Это ошибка в компиляторе или код выше вызывает некоторый UB?

OFO
источник
3
Это можно упростить еще больше .
Evg
3
ICC и MSVC также не компилируются.
ChrisMM
@Evg Удивительно , что gccкомпилирует , что, видя , как он не компилируется это ...
Макс Langhof
1
FWIW это должно быть плохо сформировано, если я не полностью ошибаюсь (по той же самой причине, что это плохо сформировано).
Макс Лангоф
1
так как мы цитируем стандарт, я добавил тег language-lawyer.
Гийом Расикот

Ответы:

3

То, что я расскажу ниже (под OLD POST ), должно быть в некоторой степени правдой, но реальная проблема в том, что SFINAE используется неправильно, поэтому я больше не уверен, что это ошибка в gcc.

Объявление псевдонима всегда должно быть успешным, вы не можете использовать SFINAE, поскольку это не объявление класса или функции или специализации (это имеет смысл, поскольку вы не можете специализировать псевдонимы). Если объявление псевдонима не удается, программа некорректна. Следовательно, компилятор может предположить, что он никогда не придет к случаю, что объявление псевдонима не будет успешным, пока вы не заставите его создать такой шаблон.

Следовательно, для компилятора вполне приемлемо думать, что sfinae_v_t<T,...>это всегда T, так как это произойдет, когда программа не будет плохо сформирована. Следовательно, он увидит, что во всех случаях, когда программа не является плохо сформированной, частичная специализация не специализируется, и как таковая она скажет вам, что это неправильно сформировано. (Это то, что делает Clang).

Я не думаю, что компилятор вынужден это делать. И если это не так, и просто думает: «Хорошо, sfinae_v_tэто какой-то тип, что угодно.», Тогда не очевидно, что это повторная декларация. Поэтому я думаю, что пока мы не создадим один из них, нет ничего плохого в том, чтобы не выдавать ошибку.

Но когда мы создаем его экземпляр, должна возникать проблема, связанная с тем, что у нас есть переопределение, или из-за неправильной формы программы std::enable_if, в зависимости от аргумента шаблона. GCC должен подобрать хотя бы один из них, но не делает ни того, ни другого.

Это также абсолютно не относится к более легкому примеру без std::enable_if. Поэтому я все еще думаю, что это ошибка в GCC, но я достаточно ошеломлен, что не могу с уверенностью сказать это. Я бы сказал, что кто-то должен сообщить об этом как об ошибке и позволить людям из gcc подумать об этом.

СТАРЫЙ ПОСТ

Это ошибка в GCC. Стандарт дает нам правила для преобразования шаблона класса в шаблоны функций. Один шаблон класса более специализирован, чем другой, если его функция предшествует другой в упорядочении шаблона частичной функции.

Я создал функции здесь, и теперь gcc заявляет, что их вызов неоднозначен, поэтому также следует сказать, что шаблоны классов заданы одинаково.

Примечание: читая стандарт внимательно, компилятор в моей голове согласен с clang.

n314159
источник
sfinae_v_t<T, std::is_integral_v<T>>И sfinae_v_t<T, !std::is_integral_v<T>>рассматриваются ли как одни и те же типы? Семантически, они не.
OFO
@GuillaumeRacicot Вполне возможно, но я бы хотел понять, почему именно. Например, стандарт также гласит: «Зависимые имена не могут быть проверены при объявлении частичной специализации, но будут проверены при замене на частичную специализацию». Разве это не означает, что один и тот же тип должен решаться после замены T в частичной специализации, поскольку sfinae_v_t<T>зависит от T? В этом случае они не будут одинаковыми, потому что любой из них будет плохо сформирован.
OFO
@ Я должен сказать, я не уверен. Даже подумать об этих двоих - это просто глупость, так как один из них никогда не будет типом, а использование обоих в нешаблонном контексте приведет к ошибке компиляции из-за enable_if_t. Мое лучшее чтение стандарта - то, что это не имеет значения, являются ли они одинаковыми или нет. Для частичного упорядочения мы всегда будем сравнивать форму параметра templare одной функции с формой аргумента шаблона другой (т. Е. intУже подставленной), а затем в одном из них будет тип realy, поэтому нам не нужно сравнивать их абстрактно.
n314159
1
Копаем глубже, я нашел это из здесь . SFINAE должен хорошо работать с псевдонимами шаблонов, иначе template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;не будет работать. Я продолжу и сообщу об ошибке в gcc, но не совсем уверен, что gcc там не так. Спасибо.
OFO