Вывод первого аргумента шаблона с другими параметрами шаблона по умолчанию
Gcc и clang, похоже, не согласны с тем, должен ли этот код компилироваться или нет:
#include <type_traits>
template <typename Signature, int N = 0>
struct MyDelegate { };
template <typename D>
struct signature_traits;
template <template <typename> class Delegate, typename Signature>
struct signature_traits<Delegate<Signature>>
{
using type = Signature;
};
static_assert(std::is_same_v<
void(int, int),
signature_traits<MyDelegate<void(int, int)>>::type
>);
Посмотрите выход godbolt здесь и попробуйте. Я нахожусь здесь с clang, но что говорит об этом стандарт С++?
Следующий вопрос - можно ли это сделать, чтобы работать в clang?
Ответы
Ответ 1
Это абсолютно правильный код, и gcc прав. "Особенность" была представленная в С++ 17. Это не особенность, потому что это отчет о дефектах. MyDelegate
соответствует частичной специализации signature_traits
, и поэтому его следует воспринимать как gcc правильно. Обратите внимание, что это работает, потому что второй параметр шаблона по умолчанию.
Причина, по которой clang не скомпилирует ее, состоит в том, что этот дефектный отчет имеет дефект: P. Он не вводит соответствующие изменения в частичном заказе, что не очень приятно и делает предыдущий действительный код неоднозначным снова.
Ожидается, что он будет исправлен в ближайшее время, но тем временем, clang решил "скрыть" функцию за флагом -frelaxed-template-template-args.
Итак, просто скомпилируйте этот флаг, и вы должны быть в порядке.
Ответ 2
Проблема заключается в том, что MyDelegate
не соответствует template <typename> class
, потому что принимает параметр два: тип (Signature
) и a int
(N
).
Да: второе имеет значение по умолчанию. Но подпись остается template <typename, int> class
.
So Я полагаю, что g++ ошибочен (компиляция без ошибок), а clang++ - это правильный. Rakete1111 исправил меня (спасибо!): Ваш код был неправильным до С++ 17, но правильно, начиная с С++ 17 (см. Его ответ на ссылки). Итак (вы компилируете С++ 17) g++ прав, а clang++ неверен.
Возможное решение (ожидающее правильного clang++) определяет signature_traits
следующим образом
template <template <typename, int=0> class Delegate, typename Signature>
struct signature_traits<Delegate<Signature>>
{
using type = Signature;
};
или, лучше IMHO, добавив целочисленный параметр
template <template <typename, int> class Delegate, typename Signature, int N>
struct signature_traits<Delegate<Signature, N>>
{
using type = Signature;
};
Заметим, что оба решения совместимы с
static_assert(std::is_same<
void(int, int),
typename signature_traits<MyDelegate<void(int, int)>>::type
>::value);