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

本文介绍了如何使用C++的type_traits和enable_if技术,实现对智能指针类型的检测,并根据检测结果决定对象的序列化行为。通过is_smart_pointer和enable_if,实现了对不同类型对象的不同操作策略,确保了序列化功能的正确应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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标准库提供的编译期判断使能某个功能的一个工具。它的原型如下:

template <bool Cond, class T = void> struct enable_if;

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;
}
### C++ 中 `type_traits` 的使用方法及示例 #### 基本概念 `<type_traits>` 是 C++ 标准库的一部分,提供了一组模板类来查询操作类型属性。这些工具允许程序员在编译期执行复杂的类型检查、转换其他元编程任务。 #### 类型特征检测 通过 `<type_traits>` 可以轻松实现不同类型特性的检测。例如: - 判断两个类型的相等性可以利用 `std::is_same<T,U>::value` 来完成[^1]。 ```cpp #include <iostream> #include <type_traits> template <typename T> void checkType() { if (std::is_same<T, int>::value) { std::cout << "T type is int\n"; } else { std://cout << "T type is not int\n"; } } ``` 此代码片段展示了如何基于传递给模板参数的具体类型做出不同行为的选择。 #### 添加额外功能 除了简单的类型比较外,还可以做更多事情,比如验证某个类型是否有特定成员函数或数据成员存在。这通常涉及到 SFINAE 技术的应用[^3]。 定义宏辅助创建自定义类型特质如下所示: ```cpp #define DEFINE_TYPE_TRAIT(name, func)\ template<typename T>\ struct name {\ template<typename Class> \ static constexpr bool test(decltype(&Class::func)*);\ template<typename> \ static constexpr bool test(...);\ \ static constexpr bool value = test<T>(0);\ };\ // 定义具体实例化版本 DEFINE_TYPE_TRAIT(has_func_foo, foo) int main(){ struct A{}; struct B{ void foo(); }; std::cout<<has_func_foo<A>::value<<"\n"; // 输出 0 表明A没有foo() std::cout<<has_func_foo<B>::value<<"\n"; // 输出 1 表明B有foo() return 0; } ``` 这段程序说明了怎样构建自己的类型特性测试器,并应用于实际场景之中。 #### 高级应用案例 当结合其他 STL 组件一起工作时,`type_traits` 更加体现出其强大之处。考虑下面的例子,在容器内部迭代过程中动态调整元素大小: ```cpp #include <vector> #include <string> #include <memory> #include <type_traits> template<class Container> auto resizeElements(Container& c, size_t newSize){ using ValueType=typename Container::value_type; if constexpr(std::is_pointer_v<ValueType>){// 如果是智能指针或其他指向对象的原始指针,则不改变它们所指向的对象尺寸 // Do nothing since we don't want to modify what pointers point at. }else{ for(auto&& elem : c){ if constexpr(!std::is_void_v<std::decay_t<decltype(*elem)>> && !std::is_array_v<std::decay_t<decltype(*elem)>>){ *elem.resize(newSize); } } } } int main(){ std::vector<std::unique_ptr<int[]>> vecPtrs={new int[5], new int[7]}; std::vector<std::string> vecStrings={"hello", "world"}; resizeElements(vecPtrs, 8); // 不会修改数组长度 resizeElements(vecStrings, 10); return 0; } ``` 这里展示了一个通用化的 `resizeElements()` 函数,它可以处理多种情况下的容器内元素重设逻辑,而无需担心破坏原有结构的数据一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值