当前位置:   article > 正文

type_traits

type_traits

 

概述

type_traits提供了丰富的编译期间计算、查询、判断、转换和选择的帮助类,其被定义在#include <type_traits>下。

作用:

  • 增强了泛型编程能力;
  • 增强程序的弹性,使得在编译期间就可以做到优化、改进甚至排错,提高代码质量;
  • 它所提供的选择功能一定程度上可以消除冗长的if-else或switch-case语句,降低程序的圈复杂度,提高代码的可维护性;
  • 它所提供的判断功能,在编译期间可以检查出是否是正确的类型,以便能编写更为安全的代码。

 

  • Helper Class

作用: 帮助创建编译时常量的标准类。

在C++11之前我们定义编译期常量的方法:

1.  通过定义静态变量,并通过CompileTimeContents::value方式获取该常量。

  1. template<typename Type>
  2. struct CompileTimeContents{
  3. static const int value = 1;
  4. };

2.  通过定义枚举变量方式。

  1. template<typename Type>
  2. struct CompileTimeContents{
  3. enum {value = 1};
  4. };

但在C++11中可以通过std::integral_constant 模版类派生:

  1. template<typename Type>
  2. struct CompileTimeContents :std::integral_constant<int, 1>
  3. {
  4. };

通过CompileTimeContents<type_name>::value方式获取该常量,这种写法好处是不用再定义额外变量,使用起来更加方便。

在标准库中的定义:

  1. template <class T, T v>
  2. struct integral_constant {
  3. static constexpr T value = v;
  4. typedef T value_type;
  5. typedef integral_constant<T,v> type;
  6. constexpr operator T() { return v; }
  7. };

 

  • 判断类型

在traits中判断类型的模板类 都 继承自 std::integral_constant

1.  判断目标类型是否为期望类型。

traits判断类型(下表展示部分,其他可到 http://www.cplusplus.com/reference/type_traits/ 查看):

traits类型说明

is_array

判断是否为数组类型

is_class

判断是否为类类型而不是union类型

is_function

判断是否为函数类型

is_reference

判断是否为引用类型

is_pod

判断是否为POD(传统Cstruct类型)

is_trivial

判断是否为内置类型

使用实例:

  1. // is_array example
  2. #include <iostream>
  3. #include <array>
  4. #include <string>
  5. #include <type_traits>
  6. int main() {
  7. std::cout << std::boolalpha;
  8. std::cout << "is_array:" << std::endl;
  9. std::cout << "int: " << std::is_array<int>::value << std::endl;
  10. std::cout << "int[3]: " << std::is_array<int[3]>::value << std::endl;
  11. std::cout << "array<int,3>: " << std::is_array<std::array<int,3>>::value << std::endl;
  12. std::cout << "string: " << std::is_array<std::string>::value << std::endl;
  13. std::cout << "string[3]: " << std::is_array<std::string[3]>::value << std::endl;
  14. return 0;
  15. }

运行结果:

判断类型通常会与std::enable_if结合使用,通过SFINAE(替换非错误)特性来实现功能更强的重载。

 

2.  判断两个模板类型之间的关系。

traits类型说明
is_same判断两个类是否相同
is_base_of判断Base类型是否为Derivedl 类型的基类
is_convertible判断前面模板参数类型能否转换为后面模板参数类型

 具体用法在官网依然有示例:

  1. // is_base_of example
  2. #include <iostream>
  3. #include <type_traits>
  4. struct A {};
  5. struct B : A {};
  6. int main() {
  7. std::cout << std::boolalpha;
  8. std::cout << "is_base_of:" << std::endl;
  9. std::cout << "int, int: " << std::is_base_of<int,int>::value << std::endl;
  10. std::cout << "A, A: " << std::is_base_of<A,A>::value << std::endl;
  11. std::cout << "A, B: " << std::is_base_of<A,B>::value << std::endl;
  12. std::cout << "A, const B: " << std::is_base_of<A,const B>::value << std::endl;
  13. std::cout << "A&, B&: " << std::is_base_of<A&,B&>::value << std::endl;
  14. std::cout << "B, A: " << std::is_base_of<B,A>::value << std::endl;
  15. return 0;
  16. }

运行结果:

 

  • 类型转换

traits中给出了对参数属性的修改接口,如:其cv属性额添加或移除、引用的添加或移除、数组维度的修改等。

traits类型说明
remove_cv移除cv属性
add_cv添加cv属性
remove_reference移除引用
add_lvaue_reference添加左值引用
remove_extent移除数组顶层维度

示例:

  1. // remove_cv example
  2. #include <iostream>
  3. #include <type_traits>
  4. int main() {
  5. typedef const volatile char cvchar;
  6. std::remove_cv<cvchar>::type a; // char a
  7. std::remove_cv<char* const>::type b; // char* b
  8. std::remove_cv<const char*>::type c; // const char* c (no changes)
  9. if (std::is_const<decltype(a)>::value)
  10. std::cout << "type of a is const" << std::endl;
  11. else
  12. std::cout << "type of a is not const" << std::endl;
  13. if (std::is_volatile<decltype(a)>::value)
  14. std::cout << "type of a is volatile" << std::endl;
  15. else
  16. std::cout << "type of a is not volatile" << std::endl;
  17. return 0;
  18. }

运行结果:

 

  • 根据条件选择traits

std::conditional 在编译期间根据判断式选择两个类型中的一个,类似于常用的条件表达式。

template <bool Cond, class T, class F> struct conditional;

          当条件为真返回T类型,条件为假返回F类型。

例如:比较long long类型与long double 返回较大的类型

  1. #include <iostream>
  2. #include <typeinfo>
  3. #include <type_traits>
  4. int main() {
  5. typedef std::conditional<(sizeof(long long) > sizeof(long double)), \
  6. long long, long double > ::type max_size_t;
  7. std::cout << typeid(max_size_t).name() << std::endl;
  8. return 0;
  9. }

运行结果:

 

  • 可调用对象返回值类型

通常我们获取函数返回值的类型使用的是decltype方式:

  1. template<typename T, typename Arg>
  2. auto Func(T a, Arg arg)->decltype(f(arg))
  3. {
  4. return f(arg);
  5. }

但是在某个类型没有模板参数时,就不能通过decltype来获取类型

  1. #include <iostream>
  2. #include <type_traits>
  3. class A{
  4. A() = delete;
  5. public:
  6. int operator()(int i){
  7. return i;
  8. }
  9. };
  10. int main() {
  11. // decltype(A()(0)) i = 4;
  12. decltype(std::declval<A>()(std::declval<int>())) i = 4;
  13. std::cout << typeid(decltype(std::declval<A>()(std::declval<int>()))).name() << std::endl;
  14. std::cout << i << std::endl;
  15. return 0;
  16. }

当用仅用decltype方式获取类型时,由于A没有默认构造函数,代码出错。

之后采用declval方式,通过 declval<A>() 获取任意类型的临时值,但临时值不能用于求值,需要借助decltype对临时值进行类型推导,得出最终返回值。

在traits中提供了另一解决途径:std::result_of

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

函数原型: 

  1. // 第一个模板参数为可调用对象类型,第二个模板参数为参数类型
  2. template <class Fn, class... ArgTypes>
  3. struct result_of<Fn(ArgTypes...)>;

 

  • ​​​​​​​根据条件禁用或启用某些类型

编译器在匹配重载函数时,通常匹配所有的重载函数,匹配一个如果失败了,编译器不会报错,而是接着匹配其他重载函数,选择最精确的一个去执行,整个过程不会报错(SFINAE)。

tratis中std::enable_if 根据限定条件选择重载函数,只对满足条件的函数有效。可作用于返回值、模板定义、类模板特化、参数类型限定。

函数原型:

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

用法示例:

  1. #include <iostream>
  2. #include <type_traits>
  3. // 1. 对函数返回值限定,只有模板参数T是integral类型时,才执行此函数
  4. template <class T>
  5. typename std::enable_if<std::is_integral<T>::value, bool>::type
  6. is_odd(T i) { return bool(i % 2); }
  7. // 2. 对模板参数限定,模板特化时,模板参数只能是intergal类型
  8. template < class T,
  9. class = typename std::enable_if<std::is_integral<T>::value>::type>
  10. bool is_even(T i) { return !bool(i % 2); }
  11. int main() {
  12. short int i = 1; // code does not compile if type of i is not integral
  13. std::cout << std::boolalpha;
  14. std::cout << "i is odd: " << is_odd(i) << std::endl;
  15. std::cout << "i is even: " << is_even(i) << std::endl;
  16. return 0;
  17. }

且通过编译期检查输入模板参数是否有效,来提前显示编译错误,避免运行时才被发现。

​​​​​​​

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/310226
推荐阅读
相关标签
  

闽ICP备14008679号