C++11(3泛型编程)——type_traits,变参函数模板

type_traits类型萃取

可以实现在编译器计算,查询,判断转换和选择
用于降低圈复杂度

基本的type_traits

1.简单的type_traits

将编译器常量包装为一个类型的type_traits----integral_constant
integral_constant中有一个常量成员变量value,可以通过integral_constant::value获取

template
struct Test : std::integral_constant {
	// 相当于
	// constexpr int value = 2;
};
int main()
{
	std::cout << Test::value << endl;
}

2.类型判断的type_traits

常用的已定义的traits很多,都是由integral_constant派生而来的。
https://en.cppreference.com/w/cpp/types

  • 用来检查模板类型是否为某种类型
  • 通过std::is_XXX::value是否为true来判断模板类型是否为目标类型。
template
struct is_integral; // is_integral是用来检查T是否无符号整型
// 如果T是的话,则is_integral::value是true
#include 
int main() {
	cout << std::is_const::value << endl; // 1
	cout << std::is_const::value << endl; // 0
}

3.判断2个类型之间的关系traits

  1. is_same
    类型严格相同才会认为是类型一致
template
struct is_same;

	cout << is_same::value << endl; // 1
	cout << is_same::value << endl; // 0
  1. is_base_of
    在编译期判断是否为继承关系
template
struct is_same;
  1. is_convertible
    前面的模板参数类型能否转换成后面的
template
struct is_convertible;

4.类型的转换traits

XXX::type表示转换后的类型

对const的修改
add_const
remove_const

对引用
remove_reference
add_lvalue_reference
add_rvalue_reference

移除数组顶层的维度
remove_extent
移除数组所有维度
remove_all_extents

remove_pointer
add_pointer

移除引用和cv符,对函数而言是添加函数指针
decay

....

conditional 根据条件选择类型

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

template
struct conditional;

result_of 获取可调用对象返回类型的traits P98

declval
result_of

enable_if 对参数类型的限定 P100

enable_if

可变参数模板

声明时,class后面加…

template
void f(T... args) {
	cout << sizeof...(args) << endl;
}

	f();            // 0
	f(1, 2);        // 2
	f(1, 2.5, "");  // 3

可变参数模板函数

展开参数包:

1. 递归函数方式展开

// 终止函数
void f() {
	cout << "end" << endl;
}

// 展开函数,设定头和剩余参数
template
void f(T head, Args... rest) {
	cout << "args:" << head << endl;
	f(rest...); // 递归调用自己,最后一次调用终止函数
}

int main() {
	f(1, 2.5, "adda");
}

在这里插入图片描述

2. 通过type_traits展开 P105

3. 逗号表达式和初始化列表方式

初始化列表方式

template
void expand(Args... args) {
    // 通过初始化列表,初始化一个变长数组
	int arr[] = { args... }; // 1,2,3,4
}

expand(1, 2, 3, 4);

逗号表达式:
实现在初始化数组的同时,操作参数

// 处理参数包中的每个参数的函数
template 
void print(T args) {
	cout << args << endl;
}

template
void expand(Args... args) {
    // 在初始化的同时,执行print函数对每个参数进行操作
	int arr[] = { (print(args), args)... }; 
	 
	 // 也可以使用initializer_list代替数组
	 std::initializer_list{ (print(args), args)...};

	// 使用匿名函数代替print函数
	// 匿名函数后的()代表着 空参 执行lamdba表达式
	std::initializer_list{ ([&](){ cout << args << endl; }(), args)...};

	// 也可以不存参数包直接传
	print(args...);
}

expand(1, 2, 3, 4);

关于逗号表达式

// 按顺序执行逗号前面的表达式
// 1. a = b
// 2. d = c
d = ( a = b , c)

可变参数模板类

1.模板递归和特化方式展开

一般需要定义2~3个类,包括类声明和特化模板类
以下例子用来计算参数类型的字节总和

// 1. 前向声明,声明Sum是可变参数模板类
template
struct Sum;

// 2.类的定义, 递归调用自己
template
struct Sum {
	enum { value = Sum::value + Sum::value };
};

// 3. 特化模板类,终止函数。参数只有一个时,value为此类型大小(只是个例子)
// 也可以写成参数只有0个或者2个时....(终止函数的写法很多)
template 
struct Sum {
	enum { value = sizeof(Last) };
};

cout << Sum::value; // 9

继承方式展开 P109

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