《C++11/14高级编程:Boost程序库探秘》笔记
模板元编程工具type_traits以库的方式实现类型特征萃取功能,成为了C++11/14标准的一部分(头文件<type_traits>),但boost.type_traits与标准并不完全一致。在boost.type_traits中,type_traits位于命名空间boost里,需要包含头文件<boost/type_traits.hpp>。
概述
type_traits库提供一组特征(traits)类——元函数,可以在编译期确定类型是否具有某些特征。
根据返回类型type_traits库里的元函数可以分为以下两大类:
- 检查元数据属性的值元函数:以::value返回一个bool值或者一个整数
- 操作元数据的标准元函数:对元数据进行计算,以::type返回一个新的元数据
type_traits库中以is_和has_开头的元函数均属于值元函数,其他则属于标准元函数:
- 检查元数据的类别:均以is_开头,都是值元函数
- 检查元数据的属性:大部分以is_和has_开头,都是值元函数
- 检查元函数之间的关系:均以is_开头,都是值元函数
- 检查操作符重载:均以has_开头,都是值元函数
- 转换元数据:都是标准元函数,返回转换后的类型
- 解析函数元数据:都是非标准元函数
- 用指定的对齐方式组合类型
元数据类别
1.基本类别
- 检查简单类别
检查简单C++类别的元函数有以下三个:
- is_integral<T> :检查T是否是bool、char、int、long等整型
- is_floating_point<T> :检查T是否是float、double、long double等浮点型
- is_void<T> :检查类型T是否为void类型
- 检查其他类别
- is_array<T> :检查T是否是一个原生数组(包括一维和多维)
- is_class<T> :检查T是否是一个class或者struct
- is_enum<T> :检查T是否是一个枚举类型
- is_union<T> :检查T是否是一个联合类型
- is_pointer<T> :检查T是否是一个指针或函数指针类型,但不是成员指针
- is_function<T> :检查T是否是一个函数类型,但不是函数指针或引用
- is_lvalue_reference<T> :检查T是否是一个左值引用类型
- is_rvalue_reference<T> :检查T是否是一个右值引用类型
- 检查成员指针类别
两个专门用于识别类成员指针类型的元函数:
- is_member_object_pointer<T> :检查T是否是指向成员变量的指针
- is_member_function_pointer<T> :检查T是否是一个成员函数指针
2.复合类别
type_traits在检查基本类别的元函数之上,提供了七个检查复合类别的元函数,相当于多个基本类别的组合,用::value返回bool类型的检查结果。
- is_reference<T> :检查T是否是一个引用类型(左引用或右引用)
- is_arithmetic<T> :检查T是否是算术类型,相当于is_integral<T> || is_floating_point<T>
- is_fundamental<T> :检查T是否是基本类型,相当于is_arithmetic<T> || is_void<T>
- is_compound<T> :检查T是否是复合类型,即非基本类型,相当于 !is_fundamental<T>
- is_member_pointer<T> :检查T是否是成员指针,包括指向数据成员和函数成员的指针,相当于is_member_object_pointer<T> || is_member_function_pointer<T>
- is_scalar<T> :检查T是否是标量类型,即算术类型、枚举、指针和成员指针
- is_object<T> :检查T是否是实体对象类型,即引用、void和函数之外的所有类型。
元数据属性
检查元数据属性的元函数都是值元函数,以is_和has_开头的元函数使用::value返回bool类型的检查结果,其他使用::value返回整数。
1.基本属性
可以判断元数据使用了哪些修饰词或语法元素
- is_const<T>
- is_volatile<T>
- is_signed<T>
- is_unsigned<T>
- rank<T> :如果T是数组,那么返回数组维数,否则返回0
- extent<T,N> 如果T是数组,那么返回数组第N个维度(从0计数)的值,否则返回0
2.类相关属性
提供了大量的元函数来检查类相关属性,较常用的有以下几个:
- is_pod<T,N> :检查T是否是一个POD类型
- is_empty<T,N> :检查T是否是一个空类
- is_abstract<T,N> :检查T是否是一个抽象类(有纯虚函数)
- is_polymorphic<T,N> :检查T是否是一个多态类(有虚函数)
- is_final<T,N> :检查T是否是一个final类(无法被继承)
3.操作符重载属性
大概有近40个检查元数据是否重载了某些操作符的值元函数(不属于C++11/14标准),比较常见的有:
- has_greater<T> :是否重载了operator >
- has_less<T>
- has_equal_to<T>
- has_plus<T>
- has_minus<T>
- has_pre_increment<T>
元数据关系
- is_same<T,U> :检查T和U是否是相同的类型
- is_convertible<From,To> :检查From是否可隐式转型为To类型
- is_base_of<B,D> :检查B是否是D的基类,或者两者相同
- is_virtual_base_of<B,D> :检查B是否是D的虚基类,不属于C++11/14标准
元数据运算
前面介绍的都是值元函数,返回bool值或整数,没有真正对元数据(类型)进行计算,下面介绍的是对于元数据的计算,输入一个类型然后输出一个新的类型。
基本运算:为C++类型添加或者删除各种修饰
- add_const<T>
- add_volatile<T>
- add_cv<T> :返回T const volatile
- add_pointer<T>
- add_lvalue_reference<T> :对象或函数类型返回T&,否则返回T
- add_rvalue_reference<T> :对象或函数类型返回T&&,否则返回T
- remove_const<T>
- remove_volatile<T>
- remove_cv<T>
- remove_pointer<T>
- remove_reference<T>
特殊运算:可处理算术类型元数据(除bool类型)、数组类型(主要操作它的维度)
- make_signed<T>
- make_unsigned<T>
- remove_extent<T> :移除数组的最顶层维度(降低一个维度)
- remove_all_extents<T> :移除数组的所有维度(变为0维的普通类型)
- conditional<b,T,U> :条件运算,类似”?:“操作,等价于mpl::if_c
- common_type<T,…> :求多个类型的共通类型
using mdata1 = int[5][7][9];
typedef remove_extent<mdata1>::type mdata2; //移除顶层维度
assert((is_same<mdata2, int[7][9]>::value));
typedef remove_all_extents<mdata1>::type mdata3;
assert((is_same<mdata3, int>::value));
typedef common_type<int, char>::type mdata4;
assert((is_same<mdata4, int>::value));
//int和std::string没有共通类型,编译错误
typedef common_type<int, std::string>::type mdata5;
解析函数元数据
解析函数元数据的元函数function_traits不属于C++11/14标准,是一个非标准元函数,能够返回多个值,包括函数的参数数量,参数类型和返回类型,支持解析最多10个参数的函数。
function_traits<T>要求输入的元数据(类型)必须满足is_function<T>,不能是函数指针或引用。如果输入的不是函数类型,那么可以用元函数remove_pointer<T>、remove_reference<T>来转换类型,否则会编译错误。
template<class T>
struct function_traits
{
static const std::size_t arity; //返回函数的参数数量
typedef some-define result_type; //返回函数的返回值类型
typedef some-define argN_type; //返回函数第N个参数的类型
};
实现原理
type_traits库里的许多值元函数都使用了元函数转发技术,把元参数转发给元函数integral_constant进行计算,也就是说integral_constant是大多数值元函数的public基类。
template<class T,T val>
struct integral_constant
{
typedef integral_constant<T, val> type; //定义自身为返回元数据
typedef T value_type; //返回值的类型
static const T value = val; //以::value返回整数值
};
type_traits库中大部分值元函数的计算结果都是bool值,所以type_traits库提供了两个针对bool元数据特化的无参元函数true_type和false_type:
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
is_integral的实现使用了模板特化,对于非整数的类型元函数总是以::value返回false,实现代码:
template<typename T>
struct is_integral : false_type {}; //元函数转发,返回false
随后,is_integral对bool、char、unsigned int、signed int等数个整数类型进行了模板特化。
应用示例
conditional
先看一个例子:输入的元数据T是指针类型则返回const T,否则返回const T*
template<typename T>
struct demo_func
{
typedef const T* type;
};
template<typename T>
struct demo_func<T*> //对T*情况进行模板特化
{
typedef const T type;
};
改写这个demo_func,不使用模板特化,而是使用元函数转发调用条件元函数conditional:
template<typename T>
struct demo_func :
conditional<is_pointer<T>::value,
typename add_const<
typename remove_pointer<T>::type
>::type,
typename add_pointer<
typename add_const<T>::type
>::type
> {};
它与boost.mpl里eval_if元函数相似,但它会自动计算参数列表里所有元函数的结果,不必再写::type,比conditional方便一些,代码可以简化:
template<typename T>
struct demo_func :
mpl::eval_if<
is_pointer<T>,
add_const<
typename remove_pointer<T>::type
>,
add_pointer<
typename add_const<T>::type
>
> {};
identity_type
identity_type是boost.utility里的一个小工具,解决C/C++的宏预处理器把逗号识别为宏参数分隔符,而导致在宏里使用带逗号的模板类参数解析错误的问题,它通过提供一个宏BOOST_IDENTITY_TYPE,把类型用一对圆括号包装起来。
std::map<int,int> m;
BOOST_FOREACH(std::pair<int,int> x,m){} //编译错误,分解为"std::pair<int" "int> x"和"m"三个参数
BOOST_FOREACH(BOOST_IDENTITY_TYPE((std::pair<int,int>)) x,m){} //编译正常
BOOST_IDENTITY_TYPE的实现代码很简单:
#define BOOST_IDENTITY_TYPE(parenthesized_type) \
boost::function_traits<void parenthesized_type >::arg1_type
使用function_traits,把宏的参数转换为一个函数,再用arg1_type返回,所以宏的参数必须要使用圆括号,否则宏展开不能成为一个函数类型。
前面代码展开后的结果是:
BOOST_FOREACH(function_traits<void (std::pair<int,int>)>::arg1_type x,m)
declval
有一个特性就是可以直接调用成员函数而不需要特别的对象构造函数
不是很理解