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

posted @ 2022-10-24 16:10  miyanyan  阅读(1393)  评论(0编辑  收藏  举报