计算机 · 2021年5月24日 0

深入应用C++11笔记 第3章

使用C++11消除重复,提高代码质量

通过实现函数和类我们可以重用代码,而通过写模板则可以写一次代码生成多个具有相似行为的函数和类,进一步精简了代码。C++11提供了更多的写模板的工具类/函数,方便我们在此基础上写出好用的模板。

类型萃取(type_traits)

C++11提供了一些模板以在编译时获得类型相关信息。

integral_constant

这些模板中的很多都是从std::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 value_type() { return value; }
};

继承自integral_constant模板类的类可以通过访问value,获取预先存在integral_constatant模板类中的值。

类型判断的type_traits

traits类型说明
template<class T>
struct is_void;
判断T是否是void类型
template<class T>
struct is_floating_point;
判断T是否为浮点类型
template<class T>
struct is_array;
判断T是否为数组修改类型
template<class T>
struct is_pointer;
判断T是否为指针类型(包括函数指针,不包括成员(函数)指针)
template<class T>
struct is_enum;
判断T是否为枚举类型
template<class T>
struct is_union;
判断T是否为union
template<class T>
struct is_class;
判断T是否为类类型而不是union
template<class T>
struct is_function;
判断T是否为函数
template<class T>
struct is_reference;
判断T是否为引用类型(左值引用或者右值引用)
template<class T>
struct is_arithmetic;
判断T是否为整型和浮点类型
template<class T>
struct is_fundamental;
判断T是否为整型、浮点、void、nullptr_t类型
template<class T>
struct is_object;
判断T是否为对象类型(不是函数、引用、void)
template<class T>
struct is_scalar;
判断T是否为arithmetic、enumeration、pointer、pointer to member、nullptr_t类型
template<class T>
struct is_compound;
判断T是否为非fundamental类型
template<class T>
struct is_member_pointer;
修改判断T是否为成员函数指针类型
template<class T>
struct is_polymorphic;
判断T是否有虚函数
template<class T>
struct is_abstract;
判断T是否为抽象类
template<class T>
struct is_signed;
判断T是否是有符号类型
template<class T>
struct is_unsigned;
判断T是否为无符号修改类型
template<class T>
struct is_const;
判断T是否有const修饰符

这些类模板都需要通过::value来访问判断结果,因为它们都派生自integral_constant

判断两个类型之间的关系

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;
判断前面的模板参数类型能否转换为后面的模板参数类型

类型转换

主要包括对const修饰符、引用、数组和指针的修改。

traits类型说明
template<class T>
struct remove_const;
移除const
template<class T>
struct add_const;
添加const
template<class T>
struct remove_reference;
移除引用
template<class T>
struct add_lvalue_reference;
添加左值引用
template<class T>
struct add_rvalue_reference;
添加右值引用
template<class T>
struct remove_extent;
移除数组顶层的维度
template<class T>
struct remove_all_extents;
移除数组所有的维度
template<class T>
struct remove_pointer;
移除指针
template<class T>
struct add_pointer;
添加指针
template<class T>
struct decay;
移除cv修饰符或添加指针,具体的规则见https://en.cppreference.com/w/cpp/types/decay
template<class… T>
struct common_type;
获取公共类型

由于这些traits是用于生成新的类型,所以需要用::value来获取结果类型。

应用例子,根据模板参数类型创建对象时需要移除引用:

template<typename T>
typename std::remove_reference<T>::type* Create()
{
  typedef typename std::remove_reference<T>::type U;
  return new U();
}

conditional

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

如果Btrue,则conditional::type为T,否则为F

获取可调用对象返回类型

模板说明
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;
获取类型T的临时值,无论类型T有没有构造函数,得到的临时值只能用于decltype判断类型,不能用于求值
template< class F, class… ArgTypes >
class result_of<F(ArgTypes…)>;
获取可调用对象的返回类型

enable_if与SFINAE

SFINAE:substitution is not an error。就是编译器在匹配函数的时候会尝试用模板生成相应的匹配更精确的函数,如果模板不能生成这样的函数,也不算错,继续匹配其他的。

模板说明
template< bool B, class T = void >
struct enable_if;
只有在B为true的时候,enable_if::type才有定义,并且为T

enable_if的定义,其实就是模板特化:

template<bool B, class T = void>
struct enable_if {};
 
template<class T>
struct enable_if<true, T> { typedef T type; };

enable_if可以用在返回值、模板定义、类模板的特化、入参类型等各种地方。

可变参数模板

在C++11之前类模板和函数模板只能含有固定数量的模板参数,而C++11允许模板定义中含有0到任意个模板参数。

省略号的作用:

  • 声明一个参数包,这个参数包中可以包含0到任意个模板参数;
  • 在模板定义里面,pattern…表达式等价于:对pattern里面的参数包的每一个参数应用pattern,然后用逗号相连。

可变参数模板函数

通过递归展开参数包

递归也是有两种,一种是模板特化的递归,一种是通过enable_if来递归。

通过parameter pack expansion展开参数包

这种方式需要结合逗号表达式和初始化列表。

可变参数模板类

与可变参数模板函数相比,可变参数模板类多了一个用继承的方式来展开参数包。

练习题

https://www.hackerrank.com/challenges/cpp-variadics/problem

可变参数模板和type_traits的综合应用

检验自己是否真的掌握了模板,看自己是否能实现作者提出的这些用模板实现的工具(其实在C++11之后的版本里有这些东西)。

  • optional
  • 惰性求值类lazy
  • dll帮助类
  • lambda链式调用
  • any
  • function_traits
  • variant
  • ScopeGuard
  • tuple_helper