Я столкнулся с этой проблемой, пытаясь специализировать 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;
}
Это ошибка, предоставленная 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?
gcc
компилирует , что, видя , как он не компилируется это ...Ответы:
То, что я расскажу ниже (под 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.
источник
sfinae_v_t<T, std::is_integral_v<T>>
Иsfinae_v_t<T, !std::is_integral_v<T>>
рассматриваются ли как одни и те же типы? Семантически, они не.sfinae_v_t<T>
зависит отT
? В этом случае они не будут одинаковыми, потому что любой из них будет плохо сформирован.enable_if_t
. Мое лучшее чтение стандарта - то, что это не имеет значения, являются ли они одинаковыми или нет. Для частичного упорядочения мы всегда будем сравнивать форму параметра templare одной функции с формой аргумента шаблона другой (т. Е.int
Уже подставленной), а затем в одном из них будет тип realy, поэтому нам не нужно сравнивать их абстрактно.template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;
не будет работать. Я продолжу и сообщу об ошибке в gcc, но не совсем уверен, что gcc там не так. Спасибо.