type_traits技术与C++

本文介绍了C++中的Type_traits技术,用于在编译期间根据数据类型特性选择不同处理方式。通过实例展示了如何利用type_traits简化模板代码,并讨论了函数调用过程、模板函数的声明与定义方式以及模板invoke的相关问题。提供了Trait技术实现迭代器的参考代码链接。

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

引言

一个方法实现过程中,业务逻辑很多都是相似的,但是与具体的特化类型的不同有一定的差异。
这个时候可以采用特化模板的方式实现,不同的类型使用不同的特化实现。但是这种情况造成一定的业务逻辑的冗余。而trait技术可以将特化类型通过封装,以一个统一的调用方式实现相同的业务逻辑。

Type_traits技术

type_traits可以翻译为类型提取器或者类型萃取器,很直白的说就是通过这个机制可以获取被操作数据类型的一些特征。这个机制在编写模板代码的时候特别有用,可以在编译期间就根据数据类型的特征分派给不同的代码进行处理。

STL中关于copy的代码

// This header file provides a framework for allowing compile time dispatch
// based on type attributes. This is useful when writing template code.
// For example, when making a copy of an array of an unknown type, it helps
// to know if the type has a trivial copy constructor or not, to help decide
// if a memcpy can be used.
struct __true_type {
};
struct __false_type {
};
template <class _Tp>
struct __type_traits { 
   typedef __true_type     this_dummy_member_must_be_first;
                   /* Do not remove this member. It informs a compiler which
                      automatically specializes __type_traits that this
                      __type_traits template is special. It just makes sure that
                      things work if an implementation is using a template
                      called __type_traits for something unrelated. */
   /* The following restrictions should be observed for the sake of
      compilers which automatically produce type specific specializations 
      of this class:
          - You may reorder the members below if you wish
          - You may remove any of the members below if you wish
          - You must not rename members without making the corresponding
            name change in the compiler
          - Members you add will be treated like regular members unless
            you add the appropriate support in the compiler. */
 
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
};
// The class template __type_traits provides a series of typedefs each of
// which is either __true_type or __false_type. The argument to
// __type_traits can be any type. The typedefs within this template will
// attain their correct values by one of these means:
//     1. The general instantiation contain conservative values which work
//        for all types.
//     2. Specializations may be declared to make distinctions between types.
//     3. Some compilers (such as the Silicon Graphics N32 and N64 compilers)
//        will automatically provide the appropriate specializations for all
//        types.
// EXAMPLE:
//Copy an array of elements which have non-trivial copy constructors
template <class T> void copy(T* source, T* destination, int n, __false_type);
//Copy an array of elements which have trivial copy constructors. Use memcpy.
template <class T> void copy(T* source, T* destination, int n, __true_type);
//Copy an array of any type by using the most efficient copy mechanism
template <class T> inline void copy(T* source,T* destination,int n) {
   copy(source, destination, n,
        typename __type_traits<T>::has_trivial_copy_constructor());
}

POD意思是Plain Old Data,也就是标量性别或者传统的C struct型别。POD性别必然拥有trivial ctor/dctor/copy/assignment 函数,因此我们就可以对POD型别采用最为有效的复制方法,而对non-POD型别采用最保险安全的方法

// uninitialized_copy
// Valid if copy construction is equivalent to assignment, and if the
//  destructor is trivial.
template <class _InputIter, class _ForwardIter>
inline _ForwardIter 
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __true_type)
{
  return copy(__first, __last, __result);
}
template <class _InputIter, class _ForwardIter>
_ForwardIter 
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
                         _ForwardIter __result,
                         __false_type)
{
  _ForwardIter __cur = __result;
  __STL_TRY {
    for ( ; __first != __last; ++__first, ++__cur)
      _Construct(&*__cur, *__first);
    return __cur;
  }
  __STL_UNWIND(_Destroy(__result, __cur));
}
template <class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter
__uninitialized_copy(_InputIter __first, _InputIter __last, _ForwardIter __result, _Tp*)
{
  typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
  return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());
}

trait技术和template 元编程的例子

template<template<int> class LOGICAL, class SEQUENCE>
struct sequence_any;

template<template<int> class LOGICAL, int NUM, int...NUMS>
struct sequence_any<LOGICAL, sequence<NUM, NUMS...> >
{
	static const bool value = LOGICAL<NUM>::value || sequence_any<LOGICAL, sequence<NUMS...>>::value;
};

template<template<int> class LOGICAL>
struct sequence_any<LOGICAL, sequence<> >
{
	static const bool value = false;
};
template<int A>
struct static_is_zero
{
	static const bool value = false;
};
template<>
struct static_is_zero<0>
{
	static const bool value = true;
};
 const bool SINGLEROWOPT = 
sequence_any<static_is_zero, sequence<SPECIALIZATIONS...>>::value;

可参考学习的C++代码

其他相关问题

函数的调用过程

如果一个程序中很多多个同名的函数,那编译器是如何找应该调用哪一个函数呢?
编译器会通过如下顺序进行查找。

  1. 函数直接匹配
  2. 模板函数
  3. 通过一定的隐形转换数据类型可以调用
#include <iostream>
void func(float a) {
  std::cout << "float func:" << a << std::endl;
}
void func(int a) {
  std::cout << "int func:" << a << std::endl;
}
template <class T>
void func(T a) {
  std::cout << "template func:" << a << std::endl;
}
int main(int argc, char const *argv[])
{
  int ia = 1;
  func(ia);
  func<int>(ia);
  float fb = 2;
  func(fb);
  func<float>(fb);
  double db = 3;
  func(db);
  func<double>(db);
  return 0;
}

结果输出

int func:1
template func:1
float func:2
template func:2
template func:3
template func:3

模板函数的声明与定义一般有两种方式

  1. 声明定义在header文件中。这种情况往往是模板针对不同的类型处理方式是一样的,这样可以直接放到头文件中。当实际调用过程中实现template的调用
  2. 声明+特化在头文件中,实际定义在cpp文件中。这种情况往往特化几种就是几种。

模板invoke模板函数

两个模板函数, 如果被调用的模板函数的只有声明在头文件中,定义与特化. 而模板的实际定义在cpp文件中,就会出现undefined的问题.

这是由于在头文件中进行调用模板函数过程中,找不到特化的被调用函数.
在头文件中显示特化声明被调用的函数, 这种情况比较适合针对不同的类型的特化有不同的处理方案.
或者直接将模板函数定义放到头文件中,这种比较适合所有的函数都适用一种情况.

### 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、付费专栏及课程。

余额充值