使用void_t的多个SFINAE类模板特化

use*_*370 22 c++ partial-specialization sfinae template-specialization c++17

多个类模板特化是否有效,当每个模板特化仅在涉及非推导上下文中的模板参数的模式之间不同时?

一个常见的例子是std::void_t使用它来定义一个特征,它揭示了一个类型是否有一个typedef名为"type" 的成员.这里采用单一专业化.这可以扩展到鉴定说类型是否有任何成员typedef被称为"TYPE1",或者一个名为"2型".下面的C++ 1z代码用GCC编译,但不是Clang.这合法吗?

template <class, class = std::void_t<>>
struct has_members : std::false_type {};

template <class T>                      
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};

template <class T>                                                        
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 8

有一条规则,部分特化必须比主模板更专业 - 您的两个专业都遵循该规则.但是没有一条规则规定部分专业化永远不会含糊不清.更重要的是 - 如果实例化导致模糊的专业化,那么该程序就会形成错误.但这种模棱两可的实例化必须首先发生!

看来,铛患的是CWG 1558这里是过于渴望约代入voidstd::void_t.

这几乎就是CWG 1980:

在一个例子中

template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
Run Code Online (Sandbox Code Playgroud)

似乎第二个声明f是对第一个声明的重新声明,但是SFINAE可以区分,即等效但不是功能等同.

如果您使用以下的非别名实现void_t:

template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
Run Code Online (Sandbox Code Playgroud)

然后clang允许两个不同的专业.当然,has_members在具有both type1type2typedef错误的类型上实例化,但这是预期的.

  • 1558只涉及替代是否适用,而不是声明匹配,这是一个完全不同的野兽.这更接近CWG 1980.别名模板的整个业务透明但不完全透明证明是棘手的:) (3认同)