C++17 之 "void_t"

C++ 17 提供了 void_t, 它是一个模板别名, 定义为

template<class...>
using void_t = void;

void_t 一般用于元编程. 从定义上看, void_t 的定义平淡无奇, 但利用 SFINAE 原则在元编程中却可以发挥巨大威力.

使用 void_t

例 1: 判断某类型是否有指定内嵌类型的模板定义

// primary template handles types that have no nested ::type member:
template< class, class = std::void_t<> >
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 { };

// 此例代码来自 http://en.cppreference.com

根据例 1, 使用例 2 的代码进行测试.

例 2: 测试

// 定义两个结构 A, B
struct A
{
    typedef int type;
};

struct B {};

//
std:: cout << std::boolalpha;

// 测试
std::cout << has_type_member<A>::value << '\n'; // prints true
std::cout << has_type_member<B>::value << '\n'; // prints false

void_t 的工作原理

下面例 3 用于判断某个类型中是否含有成员 member. 并且结合这个例子说明使用
void_t 的 SFINAE 替换过程.

例 3

// 基本模板
template<class, class = void >
struct has_member : std::false_type
{ };

// 模板特化
template<class T>
struct has_member<T, std::void_t<decltype(T::member)>> : std::true_type
{ };

// 定义两个用于测试的结构
struct A
{
    int member;
};

class B
{
};

// 测试
static_assert(has_member<A>::value); // (1)
static_assert(has_member<B>::value); // (2)

下面描述 SFINAE 替换过程.

  • 对于 (1)

    调用 has_member<A> 时, 首先选用特化模板, 发现 A::member 存在,
    decltype(A::member) 合法 (well-formed),
    std::void_t<decltype(T::member)> 演绎为 void, 所以最终演绎为
    has_member<A, void>, 从而选择了特化模板, 故 has_member<A>::vlaue
    true, 从而通过静态断言.

  • 对于 (2)

    调用 has_member<B> 时, 首先选用特化模板, 发现 b::member 不存在,
    decltype(A::member) 不合法 (ill-formed), 根据 SFINAE 原则, 不会产生
    has_member<B, some_expression> 模板代码. 编译器发现还有一个
    has_member<class, class = void> 基本模板存在, 将产生代码为
    has_member<B, void>, 而 has_member<B, void>::valuefalse, 所以第
    2 个静态断言失败.

一个有趣的例子

这个例子来自 http://purecpp.org/?p=1273, 用于判断参数类型是否为智能指针.

// 基本模板
template <typename T, typename = void>
struct is_smart_pointer : std::false_type
{
};

// 特化模板: 通过判断 T::-> 存在否和 T::get() 存在否来确定 T 是否一个智能指针
template <typename T>
struct is_smart_pointer<T,
        std::void_t<decltype(std::declval<T>().operator ->()),
                    decltype(std::declval<T>().get())>> : std::true_type
{
};

// 若对上述判断不放心, 可以再加一个条件: reset()函数. 也可以加更多的判断条件

猜你喜欢

转载自blog.csdn.net/ding_yingzi/article/details/79983042