c++11 之type_traits

1.type_traits-类型萃取

 (1)type_traits可以在一定程度上消除 switch-case 或者 if-else语句,降低程序的复杂度

(2)可以在编译期就检查出是否是正确类型

1.1基本的type_traits


定义编译期常量

  struct GetLeftSize

{

   static const int value =1;

}

 struct GetLeftSize

{

   enum { value = 1};

};

在c++11中定义编译期常量,无须自己定义static const int 或 enum类型,只需要从std::integral_constant派生:

template<typename Type>

struct GetLeftSize : std::integral_constant<int, 1>

{

};

可以通过 GetLeftSize::value来获取常量1,看下integral_constant的包装:

template<class T, T v>

struct integral_constant

{

 static const T value = v;

 typedef T value_type;

 typedef integral_constant<T, v> type;

 operator vaue_type() {return value;}

};

而true_type和false_type是integtal_constant的一个实例:

typedef integral_constant<bool, true> true_type;

typedef integral_constant<bool, false> false_type;

std::true_type和std::false_type分别定义了编译期的true和false类型


1.2类型判断的type_traits

template<class T>

struct is_integral;

检查T类型: bool char char16_t char32_t wchar_t short int long longlong,其中一种返回true。std::is_integral::value为true

is_void 

is_floating_point 浮点类型(非指针)

is_array

is_pointer 指针类型(包括函数指针,不包括成员函数指针)

is_enum

is_union 是否为非union的class/struct

is_class

is_function 是否为函数类型

is_reference 是否为引用类型(左右引用都行)

is_arithmetic是否为整型和浮点型

is_fundamental 是否为整型、浮点、void、nullptr_t

is_object 是否为一个对象类型(不是函数 不是引用 不是void)

is_member_pointer 是否为成员函数指针类型

is_polymorphic 虚函数

is_abstract 抽象类

is_signed 有符号类型

is_unsigned 无符号类型

is_const 为const修饰的类型

通过 std::is_xxx::value来判断是否满足条件

std::is_const<const int&>::value == true;

std::is_const<const int* >::value == false;


1.3 traits之判断两个类型的关系

template<class T, class U>

struct is_same; 判断两个类型是否相同


template<class Base, class Derived>

struct is_base_of; //判断Base是否为Derived的基类


template<class From, class To>

struct is_convertible; 判断前面类型是否可以转换为后面类型

#include<iostream>

#include<type_traits>

class A {};

class B : public A{};

class C{};

int mian()

{

 bool b2a = std::is_convertible<B*, A*>::value; // true

 bool a2b = std::is_convertible<A*, B*>::value; // false 不能向下转换

........

}


3.type_traits之 类型的转换

template<class T>

struct remove_const; 移除const

以下写法省略template<class T> struct

add_const

remove_reference

add_lvalue_reference

add_rvalue_reference

remove_extent // 移除数组顶层的维度

remove_all_extent //移除数组的所有维度

remove_pointer 移除指针

add_pointer 添加指针

decay 移除cv或添加指针

common_type 获取公共类型


获取转换后的类型是通过:

std::xx<>::type来获取类型的,非::value


根据模板参数类创建对象时,要注意移除引用:

template<typename T>

typename std::remove_reference<T>::type* Create()

{

   typedef typename std::remove_reference<T>::type U;

   return new U();

}

在上述例子中,返回值和函数中typename是因为模板的嵌套从属类型,好好看模板基础吧!哈哈哈~~~~

模板参数类型可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用移除


移除cv引用类型(什么鬼东西,从来没有见到过,为了满足下荣誉感,还加上吧)

template<typename T>

T* Create()

{
  return new T();

}

int* p = Create<const volatile int&>();

编译失败;需移除引用和cv符才能获取原始的类型int

template<typename T>

typename std::remove_cv<typename std::remove_reference<T>::type>::type* Create()

{

  typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type U;

  return new U();

}

代码较长,用decay来替代去除引用和cv(麻蛋,什么鬼,前面不是只说decay能去处cv,怎么又多出个引用啊,~~~):

template<typename T>

typename std::decay<T>::type* Create()

{

 typedef typename std::decay<T>::type U;

 return new U;

}

cv : const volatile

decay的功能还不止为此,还可以用于数组和函数:

先移除T类型的引用,得到类型U,U定义为remove_reference<T>::type

如果is_array<U>::value为true,修改类型type为remove_extent<U>::type*;

否则如果is_function<U>::value为true,修改类型为add_pointer<U>::type

否则修改类型为remove_cv<U>::type,去处const 或 volatile

(上面的法则真是让人想吐,那个SB定义的!!)

int&& ---int

const int& -- int

int[2] -- int*

int(int) -- int(*)(int)

函数变成函数指针可以保存下来(但是函数变成函数指针到底指的是什么鬼?就是类似int(int)变成int(*)(int)吗?)

using FnType = typename std::decay<F>::type 实现函数指针类型的定义


1.2 traits -- 根据条件选择

std::conditional 在编译期根据一个判断式选择两个类型中的一个:

template<bool B, class T, class F>

struct conditional;

如果B为true,则conditional::type为T,否则为F

比较两个类型输出较大的那个:

 typedef std::conditional<(sizeof(long long) > sizeof(long double)), long long, long double>::type max_size_t;

count<<typeid(max_size_t).name()<<endl;


1.3 traits--获取可调用对象返回类型

template<typename F, typename Arg>

?? Func(F f, Arg arg)

{

  return f(r);

}

不能直接确定返回的类型;可以通过decltype来推断:

template<typename F, typename Arg>

decltype((*(F*)0)((*(Arg*)0))) Func(F f, Arg arg)

{
  return f(arg);

}

前置的那么难理解啊?算了,还是看后置的吧

template<typename F, typename Arg>

auto Func(F f, Arg arg)->decltype(f(arg))

{

  return f(arg);

}

上述代码在没有参数的情况下,是不能通过decltype来推导类型的

#include<type_traits>

class A

   A() = delete;

public:

  int operator()(int i)

  {

    return i;

 }

};

int main ()

{

  decltype(A()(0)) i =4;

  cout<<i<<endl;

  return 0;

}

上述的代码会报错,因为A没有默认构造函数;对于没有默认构造函数的类型,如果希望推导其成员函数的返回类型,需要借助std::declval

decltype(std::declval<A>()(std::declval<int>())) i =4;

std::declval能获取任何类型的临时值,不管它是否有默认构造函数,因此,可以通过declval<A>()来获取A的临时值;

注:declval获取的临时值引用不能用于求值,因此,需要用decltype来推断出最终的返回类型

c++11提供了另外一个trait函数,用来获取一个可调用对象,std::result_of,这个真的很吊的!!!!!!!!!!!!!

std::result_of<A(int)>::type i = 4;

其实,std::result_of<A(int)>::type 实际上等价于 decltype(std::declval<A>()(std::declval<int>()))  这个真不好用,输入都费劲!!!

std::result_of的原型如下:

tempalte<class F, class...ArgTypes>

class result_of<F(ArgTypes...)>;


std::result_of<Fn(ArgTypes...)>要求Fn为一个可调用对象,不能是一个函数类型,函数类型不是一个可调用对象。

typedef std::result_of<decltype(fn)(int)>::type A; // 这种方式是错误的,这是一个函数而非可调用对象

这里说下函数对象的定义:

 1.函数指针

2.具有operator()成员函数的类对象(仿函数)

3.可被转换为函数指针的类对象

4.类成员(函数)指针

例子就不举了,反正你也看不懂,哈哈哈~~~~~~~


result_of例子走起,真尼玛牛逼,,,,,但是一般类没有构造函数的情况比较少,那么是不是用decltype比较好些,不需要转化为可调用对象

example1:

int fn(int) { return int(); }  函数

如果要对某个函数使用std::result_of,首先将函数转化为可调用对象

typedef std::result_of<decltype(fn)& (int)> ::type A;  // 这是什么鬼啊啊???

typedef std::result_of<decltype(fn)* (int)>::type B;   // 这个也看不懂????

typedef std::result_of<typename std::decay<decltype(fn)>::type(int)> ::type C;

static_assert(std::is_same<A, B>::value, "not equal"); true

static_assert(std::is_same<A, C>::value, "not equal"); true

static_assert(std::is_same<B, C>::value, "not equal"); true

example2:

template<typename Fn>

auto GroupBy(const vector<Person>& vt, const Fn& keySelector)-> multimap( decltype(keySelector((Person& ) nullptr)), Person>

{

      typedef decltype(keySelector(*(Person*) nullptr)) key_type;

     multimap<key_type, Person> map;

      std::for_each(vt.begin(), vt.end(), [&](const Person& person)

     {

       map.insert(make_pair(keySelector(person), person));

     });

    return map;

}

可以看出键值是以Fn作为函数,Person对象作为输入参数来获取类型的。

但是decltype(keySelector(*(Person*) nullptr)) key_type 比较难懂啊??????ri

用result_of来替换

template<typename Fn>

multimap<typename std::result_of<Fn(Person)>::type, Person>

GroupBy(const vector<Person>& vt, Fn&& keySelector)

{

  typedef std::result_of<Fn(Person)>:: type key_type; // Fn(Person)两个里面都是类型,到底是参数还是都行????

  multimap<key_type, Person> map;

  std::for_each(vt.begin(), vt.end(), [&] (const Person& person)

{

  map.insert(make_pair(keySelector(person), person));

});

return map;

}


1.4 traits--根据条件禁用或启用

匹配重载函数:

 template<typename T>

void Fuc(T*){ }


template<typename T>

void Fun(T){}

int main()

  Func(1);

  return 0;

}

将会匹配第二个重载函数


template<bool B, class T = void> 

struct enable_if;

在判断条件B为true时,enable_if才有效,否则的话编译失败


template<class T>

typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t)

{

  return t;

}

auto r = foo(1);

auto r1 = foo(1.2);

auto r2 = foo("tess"); // 编译错误       

std::enable_if 不仅可以作用于返回值,还可以作用于模板定义、类模板特化、入参类型的限定

// 判断第二个入参类型是否为integral类型(我感觉这句话有问题,因为判断T是否为整型,T也是第一个参数类型啊?你说是不是?)

template<calss T>

T foo2(T t, typename std::enable_if<std::is_integral<T>::value, int>::type = 0) // 默认值为0

{

  return t;

}

foo2(1,2); 可以

foo2(1," ");第二个参数错误

// 对模板参数T做了限定,T只能为integral类型

template<class T, class = typename std::enable_if<std::is_integral<T>::value>::type >

T foo3(T t)

{

  return t;

}


template<class T, class Enable = void>

class A;

// 模板特化,对模板参数做了限定,模板参数只能为浮点型

template<class T>

class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type>{};

A<double> a;

A<int> a; // 编译错误,模板参数应该为浮点型


要求:对于入参为arithmetic类型的返回 0 ,非arithmetic类型的返回1;

template<class T>

typename enable_if<std::is_arithmetic<T>::value, int> ::type foo(T t)

{

  cout<<t <<endl;

  return 0;

}

template<class T>

typename enable_if<!std::is_arithmetic<T>::value, int>::type foo(T& t)

{

  cout<<typeid(T).name()<<endl;

 return 1;

}

std::enable_if的第二个参数是默认模板参数void类型,因此在函数没有返回值时,后面的模板参数可以省略

typename std::enable_if<std::is_arithmetic<T>::value>::type fool(T t)  //返回值为void, 前面是为了检查类型是否符合条件

{

  cout<<typeid(T).name()<<endl;

}

typename std::enable_if<std::is_same<T, std::string>::value>::type foo1(T& t)

{

 cout<<typeid(T).name()<<endl;

}

可以看到std::enable_if的强大重载机制,即使返回值相同也可以重载,(NIMA当我瞎啊,入参类型不是不一样吗?引用到底影不影响啊?????)

下面来一段牛逼的代码:

template<typename T>

string ToString(T t)

{

  if(typeid(T) == typeid(int) || typeid(T) == typeid(double) || typeid(T) == typeid(float) )

{

  std::string stream ss;

  ss<<value;  // 不要问我,这句话我也看不懂??????????????

 return ss.str();

else if(typeid(T) == typeid(string))

{

 return t;

}

}

用enable_if来替换:

template<class T>

typename std::enable_if<std::is_arithmetic<T>::value, string>::type

  ToString(T& t) { return std::to_string(t); }

tempalte<class T>

typename std::enable_if<std::is_same<T, std::string>::value, string>::type

 ToString(T& t) { return t; }



### 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()` 函数,它可以处理多种情况下的容器内元素重设逻辑,而无需担心破坏原有结构的数据一致性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值