天天看点

C++ type_traits 和 enable_if 应用示例,用模板元编程判断传入的参数是否为智能指针

C++ type_traits、enable_if应用示例

    • 一、type_traits简介
    • 二、type_traits实现is_smart_pointer
    • 三、enable_if简介

一、type_traits简介

type traits简介:类型萃取就是使用C++模板编程重载时的特性来实现特定类型特定行为的操作。

这个重载时的特性主要就是指在重载匹配决议过程中,编译器会优先匹配更精确的类型,也就是如果特化模板匹和泛化模板同时匹配上了类型推导,编译器会优先选择特化模板进行重载匹配。

特定类型特定行为则是指我们让符合要求的类型匹配到和不符合要求的类型匹配到不同的操作,这样就实现了符合要求的类型发生了特定的行为,而不符合要求的类型发生了另外一种行为。

类型萃取的应用有很多,举一个例子,现在我们有一个问题,对象序列化,我们知道C++本身不提供对象的序列化操作,有一些开发者在设计类的时候考虑到某些类可能有序列化的需要,于是设计了该类的序列化接口,而另一些类很大概率上没有序列化应用的必要,或者开发者在设计类时没有考虑其序列化的功能,现在我们有一些源码,我们也知道哪些类设计了序列化接口,哪些没有涉及,现在我们要设计一个统一的序列化函数,这个函数是一个函数模板,它接受一个对象作为参数,我们要求这个函数接收的如果是一个带有序列化接口的函数,我们就返回其序列化的结果,如果接受的对象没有提供序列化的接口,我们就让这个程序编译不通过或者采取别的行为,那么我们就实现了特定类型(有序列化接口的类型)发生了特定行为(返回序列化的结果),而其它类型则不会发生该行为。

二、type_traits实现is_smart_pointer

我们根据 cppreference 上 is_pointer 类模板可以尝试实现我们的 is_smart_pointer:

template<class T>
struct is_pointer_helper : std::false_type {};
 
template<class T>
struct is_pointer_helper<T*> : std::true_type {};
 
template<class T>
struct is_pointer : is_pointer_helper< typename std::remove_cv<T>::type > {};
           

从上面可以看出,当类型推导为T和T*都符合匹配时替换原则的话,编译器会选择匹配更精确的,也就是匹配上特化模板,而该特化模板继承了 true_type,所以其中有一个static bool变量value值为true,然后类模板 is_pointer 继承了is_pointer_helper,当推导出的类型T为匹配特化的is_pointer_helper时,is_pointer继承的是继承了true_type的is_pointer_helper,当推导出的类型T为不匹配特化的is_pointer_helper时,is_pointer继承的是继承了false_type的is_pointer_helper,这样我们就使用编译期的重载决议帮助我们确定了每一个实例化的is_pointer模板类的成员value对应着指示其推测的类型是否为指针类型。

根据以上is_pointer,我们可以尝试实现我们的is_smart_pointer_helper和is_smart_pointer,代码如下:

template <typename T>
struct is_smart_pointer_helper : public false_type {};

template <typename T>
struct is_smart_pointer_helper<shared_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<unique_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<weak_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<auto_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer : public is_smart_pointer_helper<typename remove_cv<T>::type> {};
           

三、enable_if简介

enable_if是一个c++11标准库提供的编译期判断使能某个功能的一个工具。它的原型如下:

C++ reference上解释:

The type T is enabled as member type enable_if::type if Cond is true.

Otherwise, enable_if::type is not defined.
           

也就是说,只有当enable_if传入的第一个模板参数值为true的时候,enable_if类中的static成员变量type才会被赋值为T类型,否则,该type变量是未定义的。事实上,该type不仅仅是未定义的,还是会导致替换失败的,也就是当cond不为true的时候,type没有被赋值,则type也不能被使用,这导致匹配失败,而这时如果没有其它可以匹配的类模板,则会出错。这样也就实现了这个类名字的字面含义,也就是enable_if可以理解为 enable type = T if Cond,C++ reference上有一个示例:

// enable_if example: two ways of using enable_if
#include <iostream>
#include <type_traits>

// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
  is_odd (T i) {return bool(i%2);}

// 2. the second template argument is only valid if T is an integral type:
template < class T,
           class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}

int main() {

  short int i = 1;    // code does not compile if type of i is not integral

  std::cout << std::boolalpha;
  std::cout << "i is odd: " << is_odd(i) << std::endl;
  std::cout << "i is even: " << is_even(i) << std::endl;

  return 0;
}
           

我们可以通过以上两种形式使用enable_if,一种是作为返回值类型(示例中1.),如果Cond,也就是示例1.中的is_integeral::value为true时,则type使能为bool类型,该重载匹配替换没有问题,正常执行,但如果Cond,也就是示例1.中的is_integeral::value为false时,则type是未定义的,该模板匹配替换失败,考虑其它可能的匹配替换,如果其它的也失败,则编译报错无法匹配。另外一种则是如示例2.将type写在template 的typename列表里,当is_even推断出的T满足使is_integral的value成员为true时,type则被赋值(使能)为void,这里enable_if只传入了一个is_integral::value到Cond形参中,则enable_if中的T被默认设置为void,则type也被使能为void,虽然type被赋值为void,但它还是被使能了的,也就是重载决议时可以匹配替换,但是若is_integral::value值为false,则type不能被使能,这使得该模板替换失败,继续寻找其它可能匹配的重载模板。

仿照上述enable_if的使用方法,我们判断一个传入的参数是否为智能指针:

template <typename T>
typename enable_if<is_smart_pointer<T>::value,void>::type check_smart_pointer(const T& t)
{
    cout << "is smart pointer" << endl;
}

template <typename T>
typename enable_if<!is_smart_pointer<T>::value,void>::type check_smart_pointer(const T& t)
{
    cout << "not smart pointer" << endl;
}

int main()
{
    int* p(new int(2) );
    shared_ptr<int> pp(new int(2) );
    unique_ptr<int> upp(new int(4) );
    check_smart_pointer(p);
    check_smart_pointer(pp);
    check_smart_pointer(upp);
    return 0;
}
           

我们将两处代码合并到一起,有:

#include <iostream>
#include <type_traits>
#include <memory>

using namespace std;

template <typename T>
struct is_smart_pointer_helper : public false_type {};

template <typename T>
struct is_smart_pointer_helper<shared_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<unique_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<weak_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer_helper<auto_ptr<T> > : public true_type {};

template <typename T>
struct is_smart_pointer : public is_smart_pointer_helper<typename remove_cv<T>::type> {};

template <typename T>
typename enable_if<is_smart_pointer<T>::value,void>::type
	check_smart_pointer(const T& t)
{
    cout << "is smart pointer" << endl;
}

template <typename T>
typename enable_if<!is_smart_pointer<T>::value,void>::type
	check_smart_pointer(const T& t)
{
    cout << "not smart pointer" << endl;
}

int main()
{
    int* p(new int(2) );
    shared_ptr<int> pp(new int(2) );
    unique_ptr<int> upp(new int(4) );
    check_smart_pointer(p);
    check_smart_pointer(pp);
    check_smart_pointer(upp);
    return 0;
}
           

继续阅读