我不明白问题出在哪里:无论是在我的代码中还是在编译器中(不太可能)。有一段这样的代码:
#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均对其进行编译。我使用不同版本的GCC和MSVC 17(本地)和19在Godbolt上进行了测试。这是一个链接:https://godbolt.org/z/Enfm6L。
但是Clang不会编译它并发出错误:
redefinition of `'TestA<T, std::void_t<typename T::dummy_iterator> >'`
我很感兴趣-也许在标准的某些部分中,这段代码是不正确的,或者还有其他地方。
答案 0 :(得分:9)
这很可能与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 等同于简单地作废,还是替代失败?
这是一个已解决的缺陷,但是,如果您使用的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令人安心。