c++ template std::void_t 是个啥
源码
在<type_traits>
头文件里有void_t的定义
template <class... _Types>
using void_t = void;
很简单,其实就是void,只不过可以传入模板参数,比如std::void_t<int, float, double>
,但归根到底他还是void
有什么用
那他有啥用,看上去像是一段无用的程序
这里就用到SFINAE(Substitution failure is not an error)的知识了,我们分三步走
-
先看这个问题: 下面有三种foo函数,称其为Foo0,Foo1,Foo2,请问 foo
foo foo 分别匹配到了哪个函数? struct X { typedef int type; }; struct Y { typedef int type2; }; template <typename T> void foo(typename T::type); // Foo0 template <typename T> void foo(typename T::type2); // Foo1 template <typename T> void foo(T); // Foo2 foo<X>(5); // Foo0: Succeed, Foo1: Failed, Foo2: Failed foo<Y>(10); // Foo0: Failed, Foo1: Succeed, Foo2: Failed foo<int>(15); // Foo0: Failed, Foo1: Failed, Foo2: Succeed
对于foo
,因为X有type,所以直接匹配到了Foo0
对于foo,因为Y没有typ,所以匹配Foo0失败,尝试匹配下一个,Y有type2,成功匹配到了Foo1
对于foo,因为int既没有type有没有type2,所以Foo0和Foo1都匹配失败,最终匹配到了Foo2
看到了吧,虽然有的地方匹配失败了,但是编译器并不会直接报错,他会尝试去匹配其他的 -
回到std::void_t,通过上一点可知,我们对他的定义应该更严谨 : 如果传进来的模板参数是正常的,那std::void_t就是void;但是如果传进来的模板产生了错误,std::void_t会产生匹配错误,编译器您去找别人吧...
-
将 SFINAE 和 std::void_t 结合,我们可以得到任意类型的不同匹配分支
// primary template handles types that have no nested ::type member: template< class, class = void > struct has_type_member : std::false_type { }; // specialization recognizes types that do have a nested ::type member: template< class T > struct has_type_member<T, std::void_t<typename T::type>> : std::true_type { }; auto f = has_type_member<float>::value; // false auto x = has_type_member<X>::value; // true auto y = has_type_member<Y>::value; // false
对于
has_type_member<float>
,因为float没有type,所以第二个模板会匹配失败,编译器会认为现在的has_type_member<float>
实际是has_type_member<float, void>
(因为void是个默认参数),进而成功匹配第一个模板
对于has_type_member<X>
,因为X有type,第二个模板直接匹配成功,编译器会认为这是第一个模板的一个特化版本
这里std::void_t的作用,就是把传进来的各个类型进行一个检测,哦没错过了吧!诶不对传进来的有问题编译器你快过来看看!
实际使用
判断stream是否有operator<<
template <class T0, class T1, class = void>
struct _has_stream_bit_shl : std::false_type {};
template <class T0, class T1>
struct _has_stream_bit_shl<T0, T1, std::void_t<
decltype(std::declval<T0>() << std::declval<T1>())
>> : std::true_type {};
auto a = _has_stream_bit_shl<std::ostream&, const int&>::value; // true
auto b = _has_stream_bit_shl<std::ostream&, const X&>::value; // false
参考:https://stackoverflow.com/questions/27687389/how-does-void-t-work