使用void_t的多个SFINAE类模板专业化
Multiple SFINAE class template specialisations using void_t
当多个类模板专门化仅在非推导上下文中涉及模板参数的模式之间不同时,它们是否有效?
std::void_t
的一个常见示例使用它来定义一个特征,该特征揭示一个类型是否有一个名为"类型"的成员typedef
。在这里,采用了单一的专业化。这可以扩展到识别一个类型是否有一个名为"type1"的成员typedef
,或者一个名"type2"的成员。下面的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 {};
有一条规则,部分专业化必须比主模板更专业化-两个专业化都遵循该规则。但是,并没有一条规则规定部分专业化永远不能模棱两可。更重要的是,如果实例化导致了不明确的专业化,那么程序就是格式错误的。但这种模棱两可的实例化必须首先发生!
clang似乎患有CWG1558,并且过于渴望用。void
代替std::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();
f
的第二个声明似乎是对第一个声明的重新声明,但可通过SFINAE区分,即等效但功能上不等效。
如果使用void_t
:的非别名实现
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
clang允许两种不同的专业化。当然,在同时具有type1
和type2
的类型上实例化has_members
会导致typedef错误,但这是意料之中的事。
我不认为这是正确的,或者至少,如果我们用嵌套了type1和type2的类型实例化has_members,结果将是两个
has_members<T, void>
这将是无效的。在代码被实例化之前,我认为它是可以的,但clang很早就拒绝了它。在g++上,这个用例一旦实例化就会失败:
struct X
{
using type1 = int;
using type2 = double;
};
int main() {
has_members<X>::value;
}
错误消息似乎并没有描述实际的问题,但至少发出了:
<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
has_members<X>::value;
^~~~~
如果您用一个只有type1或type2但不同时具有这两种类型的类型实例化它,然后g++干净地编译它。因此,它反对成员都存在的事实,从而导致模板的实例化冲突。
为了得到析取,我想你会想要这样的代码:
template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};
template <class T>
struct has_members<T, std::enable_if_t<
std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> :
std::bool_constant<true> {};
这假设您有特征来确定已经编写的has_member_type1和has_membel_type2。
- 如何使用默认参数等选择模板专业化
- 模板化建造师专业化
- 为什么使用SFINAE而不是函数重载
- 类模板的成员功能的定义在单独的TU中完全专业化
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 提供与TMP和SFINAE的通用接口
- 部分专业化和嵌套模板
- 模板专业化可以进入我的.cpp吗?
- "Inverse SFINAE"避免模棱两可的过载
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- 是否可以混合使用SFINAE和模板专业化?
- 使用void_t的多个SFINAE类模板专业化
- 部分模板专业化 SFINAE
- 部分专业化和SFINAE
- variadic模板专业化,std :: enable_if,sfinae
- 使用 SFINAE、约束或概念限制专业化
- 禁用默认模板,仅通过sfinae使用专业化