Играясь, я наткнулся на следующий фрагмент кода, который на удивление не соответствует моим ожиданиям.
#include <tuple>
#include <type_traits>
template <class... Ts>
using myTuple = std::tuple<Ts...>;
template <template <class...> class Lhs, template <class...> class Rhs>
struct is_same
: public std::false_type
{};
template <template <class...> class T>
struct is_same<T, T>
: public std::true_type
{};
int main() {
static_assert(is_same<myTuple, std::tuple>::value, "not same");
}
Я закинул его на godbolt и попытался скомпилировать тремя разными компиляторами (clang, msvc и gcc) и получил смешанные результаты.
лязг:
<source>:18:5: error: static_assert failed due to requirement 'is_same<myTuple, std::tuple>::value' "not same"
static_assert(is_same<myTuple, std::tuple>::value, "not same");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
МСВК:
<source>(18): error C2338: static_assert failed: 'not same'
gcc:
(no errors)
Теперь я задаюсь вопросом, какое поведение является правильным? Я ожидаю, что оба аргумента одинаковы, и поэтому используется специализация, потому что псевдонимы обычно не отличаются от исходного типа. Если это не правильный аргумент здесь, я хотел бы знать, почему.
GCC ошибается в этом, а Clang и MSVC правы. static_assert
должен потерпеть неудачу.
myTuple
не является псевдонимом шаблона std::tuple
. Псевдонимов для шаблонов нет, только для типов.
std::tuple
и myTuple
— это два разных шаблона, один из которых является шаблоном класса, а другой — шаблоном псевдонима. Шаблоны псевдонимов не называются так, потому что они являются псевдонимами других шаблонов. Их так называют
потому что каждая специализация шаблона псевдонима является псевдонимом (типа).
То, что каждая специализация двух шаблонов с одним и тем же списком аргументов шаблона приводит к тому, что что-то ссылается на один и тот же тип, здесь не должно иметь значения.
Вывод по перегрузке true
должен завершиться ошибкой при выводе двух разных значений (т. е. шаблонов) для T
.
Но см., например. открытый (но старый) CWG issue 1286 , в котором рассматривается возможность сделать определенные шаблоны псевдонимов формы, которую вы используете, эквивалентными шаблону класса, используемому в его определении. Я предполагаю, что это также должно повлиять на вывод аргумента шаблона, чтобы рассмотреть два значения, выведенные для T
, «согласованные». Эта проблема также связана с решенной проблемой CWG 1244, которая подтверждает, что псевдоним и шаблон класса не эквивалентны и поэтому (я бы сказал) также не «согласованы» с целью вывода аргументов шаблона.
@AdrianMole Нет, все компиляторы (или, по крайней мере, GCC и Clang, которые я могу протестировать) согласны с тем, что это
std::is_same<myTuple, std::tuple>::value
неправильно сформировано, а не с тем, что оно ложно. ОП пришлось сворачивать свои собственныеis_same
, посколькуstd::is_same
может сравнивать только типы, а не шаблоны.