type_traits

 

概述

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

作用:

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

 

  • Helper Class

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

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

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

    template
    struct CompileTimeContents{
        static const int value = 1;
    };

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

    template
    struct CompileTimeContents{
       enum {value = 1};
    };

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

    template
    struct CompileTimeContents :std::integral_constant
    {
    };

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

在标准库中的定义:

    template 
    struct integral_constant {
      static constexpr T value = v;
      typedef T value_type;
      typedef integral_constant type;
      constexpr operator T() { return v; }
    };

 

  • 判断类型

在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

判断是否为内置类型

使用实例:

// is_array example
#include 
#include 
#include 
#include 

int main() {
  std::cout << std::boolalpha;
  std::cout << "is_array:" << std::endl;
  std::cout << "int: " << std::is_array::value << std::endl;
  std::cout << "int[3]: " << std::is_array::value << std::endl;
  std::cout << "array: " << std::is_array>::value << std::endl;
  std::cout << "string: " << std::is_array::value << std::endl;
  std::cout << "string[3]: " << std::is_array::value << std::endl;
  return 0;
}

运行结果:

type_traits_第1张图片

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

 

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

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

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

// is_base_of example
#include 
#include 

struct A {};
struct B : A {};

int main() {
  std::cout << std::boolalpha;
  std::cout << "is_base_of:" << std::endl;
  std::cout << "int, int: " << std::is_base_of::value << std::endl;
  std::cout << "A, A: " << std::is_base_of::value << std::endl;
  std::cout << "A, B: " << std::is_base_of::value << std::endl;
  std::cout << "A, const B: " << std::is_base_of::value << std::endl;
  std::cout << "A&, B&: " << std::is_base_of::value << std::endl;
  std::cout << "B, A: " << std::is_base_of::value << std::endl;
  return 0;
}

运行结果:

type_traits_第2张图片

 

  • 类型转换

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

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

示例:

// remove_cv example
#include 
#include 

int main() {
  typedef const volatile char cvchar;
  std::remove_cv::type a;       // char a
  std::remove_cv::type b;  // char* b
  std::remove_cv::type c;  // const char* c (no changes)

  if (std::is_const::value)
    std::cout << "type of a is const" << std::endl;
  else
    std::cout << "type of a is not const" << std::endl;

  if (std::is_volatile::value)
    std::cout << "type of a is volatile" << std::endl;
  else
    std::cout << "type of a is not volatile" << std::endl;

  return 0;
}

运行结果:

type_traits_第3张图片

 

  • 根据条件选择traits

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

template  struct conditional;

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

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

#include 
#include 
#include 

int main() {
	typedef std::conditional<(sizeof(long long) > sizeof(long double)), \
		long long, long double > ::type max_size_t;
	std::cout << typeid(max_size_t).name() << std::endl;

	return 0;
}

运行结果:

type_traits_第4张图片

 

  • 可调用对象返回值类型

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

template
auto Func(T a, Arg arg)->decltype(f(arg))
{
    return f(arg);
}

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

#include 
#include 

class A{
	A() = delete;

public:
	int operator()(int i){
		return i;
	}
};

int main() {
	// decltype(A()(0)) i = 4;
	decltype(std::declval()(std::declval())) i = 4;
	
	std::cout << typeid(decltype(std::declval()(std::declval()))).name() << std::endl;
	std::cout << i << std::endl;

	return 0;
}

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

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

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

	std::result_of::type i = 4;

函数原型: 

    // 第一个模板参数为可调用对象类型,第二个模板参数为参数类型
    template  
    struct result_of;

 

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

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

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

函数原型:

    template  
    struct enable_if;

用法示例:

#include 
#include 

// 1. 对函数返回值限定,只有模板参数T是integral类型时,才执行此函数
template 
typename std::enable_if::value, bool>::type
is_odd(T i) { return bool(i % 2); }


// 2. 对模板参数限定,模板特化时,模板参数只能是intergal类型
template < class T,
class = typename std::enable_if::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;
}

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

​​​​​​​

你可能感兴趣的:(C++)