Clang не компилирует код, но g cc и msv c скомпилировали его - PullRequest
14 голосов
/ 19 февраля 2020

Я не понимаю, в чем проблема: ни в моем коде, ни в компиляторе (менее возможно). Существует фрагмент кода, подобный следующему:

#include <iostream>
#include <type_traits>
#include <set>


template<typename T, typename = void>
struct TestA: std::false_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::reverse_iterator>> : std::true_type {};

template<typename T>
struct TestA<T, std::void_t<typename T::dummy_iterator>> : std::true_type {};

int main()
{
    std::cout << TestA<std::set<int>>::value;
}

И G CC, и MSV C компилируют его. Я тестировал его на Godbolt с другой версией G CC и MSV C 17 (локальный) и 19. Вот ссылка: https://godbolt.org/z/Enfm6L.

Но Clang не делает t скомпилирует его и выдаст ошибку:

redefinition of `'TestA<T, std::void_t<typename T::dummy_iterator> >'`

И мне интересно - может быть, есть какая-то часть стандарта, где этот фрагмент кода неверен, или может быть что-то еще.

1 Ответ

9 голосов
/ 19 февраля 2020

Скорее всего, это связано с CWG 1558 .

Обработка неиспользованных аргументов в специализации шаблона псевдонима не определена текущей формулировкой 17.6.7 [temp .alias]. Например:

  #include <iostream>

  template <class T, class...>
    using first_of = T;

  template <class T>
    first_of<void, typename T::type> f(int)
      { std::cout << "1\n"; }

  template <class T>
    void f(...)
      { std::cout << "2\n"; }

  struct X { typedef void type; };

  int main() {
    f<X>(0);
    f<int>(0);
  }

Является ли ссылка на first_of с T эквивалентной int как просто void, или это ошибка замещения?

Это ошибка, которая с тех пор была устранена, но если версия Clang, которую вы использовали, еще не внедрила это исправление, она все равно может рассматривать обе специализации как простое определение второго аргумента как void, а не весь спил неудачи замещения. Обходной путь состоит в том, чтобы не использовать простой псевдоним std::void_t, а вместо этого несколько более сложную версию

template <typename...> struct voider { using type = void; };
template <typename... T> using my_void_t = typename voider<T...>::type;

Для шаблона класса (то, что теперь обозначает псевдоним), определяется ошибка замещения. Включение этого в ваш пример ублажает Clang https://godbolt.org/z/VnkwsM.

...