Я не понимаю, в чем проблема: в моем коде или в компиляторе (менее возможно). Вот такой кусок кода:
#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;
}
И GCC, и MSVC компилируют его. Я тестировал его на Godbolt с разными версиями GCC и MSVC 17 (локальная) и 19. Вот ссылка: https://godbolt.org/z/Enfm6L .
Но Clang не компилирует его и выдает ошибку:
redefinition of `'TestA<T, std::void_t<typename T::dummy_iterator> >'`
И мне интересно - может быть, есть какая-то часть стандарта, где этот фрагмент кода неправильный, или, может быть, что-то еще.
Ответы:
Это очень вероятно связано с CWG 1558 .
Это дефект, который с тех пор был устранен, но если версия Clang, которую вы использовали, еще не внедрила это исправление, она все равно может рассматривать обе специализации как простое определение второго аргумента
void
, а не весь спил ошибки замещения. Обходной путь - не использовать простой псевдонимstd::void_t
, а немного более сложную версию.Для шаблона класса (то, что теперь обозначает псевдоним), определяется ошибка замещения. Включение этого в ваш пример успокаивает Clang https://godbolt.org/z/VnkwsM .
источник
enable_if
сstd::disjunction
(или с требованием)