10 C++11

使用GNU C++编译器(g++)编译包含C++11语法特性的代码,需要加上“-std=c++11”编译选项。例如:

10.1 简化代码

10.1.1 类型推导

10.1.1.1 auto

10.1.1.1.1 auto的基本用法
10.1.1.1.2 auto同指针或引用结合使用
10.1.1.1.3 auto使用的限制
10.1.1.1.4 何时使用auto?

10.1.1.2 decltype

10.1.1.2.1 计算表达式的类型
10.1.1.2.2 类型计算规则
10.1.1.2.3 何时使用decltype?
10.1.1.2.4 返回类型后置

10.1.2 模板改进

10.1.2.1 为模板定义别名

10.1.2.1.1 不能用typedef为模板定义别名
10.1.2.1.2 可以用using为包括模板在内的任何类型定义别名

10.1.2.2 函数模板的缺省模板参数

10.1.2.2.1 C++98/03中的函数模板不能带有缺省模板参数

在C++98/03中,只有类模板可以带有缺省模板参数,函数模板不能带有缺省模板参数。

10.1.2.2.2 C++11中的类模板和函数模板都可以带有缺省模板参数

10.1.2.3 模板实参表的右尖括号

10.1.2.3.1 C++98/03标准模板实参表的右尖括号

在C++98/03标准中,连续的两个右尖括号会被编译器解释为右移位操作符,而非模板实参表结束,因此它们之间至少应保留一个空格。

10.1.2.3.2 C++11标准模板实参表的右尖括号

在C++11标准中,要求编译器对模板的右尖括号做单独处理,使之能正确区分右移位操作符和模板实参表结束,因此无需再留空格。

10.1.3 列表初始化

10.1.3.1 C++98/03标准没有通用的初始化语法

在C++98/03中,对象初始化方法有很多种,每一种都有各自的适用范围和作用。没有任何一种初始化方法可以通用于所有情况。

10.1.3.2 C++11标准提供了通用的列表形式的初始化语法

在C++11中,列表形式的初始化语法被大大扩充了,可用于任何类型对象及其数组的初始化,无论该对象或数组是被静态还是动态创建的。

10.1.3.3 聚合类型与非聚合类型的列表初始化

何为聚合类型?

对聚合类型使用列表初始化,相当于对其中的元素逐一初始化,而对非聚合类型使用列表初始化,则需要调用匹配的构造函数。

10.1.3.4 变长初始化列表

10.1.3.5 initializer_list轻量级类模板容器

10.1.3.6 借助列表初始化防止类型收窄

10.1.4 范围循环

10.1.4.1 基于范围的for循环

10.1.4.2 范围循环的注意事项

10.1.4.3 使自定义类型支持基于范围的for循环

10.1.5 函数绑定

10.1.5.1 可调用对象与函数对象

10.1.5.2 函数绑定器

10.1.5.3 绑定类的成员函数

10.1.5.4 新版本的bind简化和强化了老版本的bind1st和bind2nd

10.1.6 匿名函数

10.1.6.1 lambda表达式

lambda表达式定义了一个匿名函数,其本质是一个匿名的仿函数对象,亦可被视为第4种形式的可调用对象。

10.1.6.2 lambda表达式的捕获表

10.1.6.3 借助lambda表达式简化对标准库算法的调用

10.1.7 泛型元组

tuple可以理解为是对老版本pair的增强和扩展,其中的元素个数不再限于两个,功能也更加丰富。

10.2 提高性能

10.2.1 右值引用

10.2.1.1 左值和右值

10.2.1.2 左值引用和右值引用

10.2.1.3 通用引用

10.2.1.4 引用折叠

C++98/03标准不允许声明引用的引用,但C++11允许这么声明,并根据引用折叠规则将其处理为简单引用的形式:只有右值引用的右值引用还是右值引用,其它情况下的叠加都是左值引用。

10.2.1.5 move和forward

10.2.2 移动语义

10.2.2.1 支持浅拷贝的构造函数

10.2.2.2 支持深拷贝的构造函数

10.2.2.3 支持移动语义的构造函数

10.2.2.4 同时提供支持深拷贝和移动语义的构造函数和赋值操作符函数

同时提供支持深拷贝和移动语义的构造函数和赋值操作符函数,编译器会在适当的时候选择适当的版本,在保证功能的同时提高性能。

10.2.3 就地构造

几乎所有标准库容器都提供了类似emplace_back、emplace_front和emplace这样的接口,以作为传统的push_back、push_front和insert接口的替代方案。相较于后者,前者采用在容器内部就地构造对象的方式,以尽可能避免过多的内存复制和移动,具有更好的运行时性能。

10.2.4 无序容器

C++11在C++98/03所提供的四个基于红黑树的有序关联容器,map、multimap、set和multiset的基础上,又增加了四个基于散列表的无序关联容器,unordered_map、unordered_multimap、unordered_set和unordered_multiset。与前者相比,后者通过哈希而非排序组织其中的元素,因而效率更高。

10.3 避免重复

10.3.1 类型萃取

10.3.1.1 类的编译期常量

10.3.1.1.1 C++98/03标准为类定义编译期常量的方法
10.3.1.1.2 C++11标准为类定义编译期常量的方法

在C++11中,为一个类定义编译期常量,既无需自己定义常静态成员变量,亦无需提供枚举成员类型,只要从integral_constant基类继承即可。

10.3.1.1.3 integral_constant类模板的简化实现
10.3.1.1.4 编译期布尔类型

10.3.1.2 编译期类型和类型关系判断

10.3.1.2.1 编译期类型判断
当且仅当实例化模板的类型实参为......模板实例化类中的布尔型静态成员value的值为true
空类型is_void
整数类型is_integral
浮点数类型is_floating_point
指针类型(不包括成员指针)is_pointer
成员指针类型is_member_pointer
引用类型is_reference
数组类型is_array
枚举类型is_enum
联合类型is_union
类类型(包括结构体)is_class
多态类型(含虚函数)is_polymorphic
抽象类型(含纯虚函数)is_abstract
函数类型is_function
算术类型(整数、浮点数)is_arithmetic
基本类型(空、整数、浮点数、指针)is_fundamental
复合类型is_compound
对象类型(除空、引用、函数外)is_object
标量类型(空、指针、成员指针、枚举、算术)is_scalar
有符号类型is_signed
无符号类型is_unsigned
常类型is_const
············
10.3.1.2.2 编译期类型关系判断
当且仅当实例化模板的两个类型实参满足......模板实例化类中的布尔型静态成员value的值为true
相同类型is_same
第一个是第二个的基类is_base_of
第一个能被隐式转换为第二个is_convertible
············

10.3.1.3 编译期类型转换及其应用

10.3.1.3.1 编译期类型转换
用特定类型实参实例化模板...模板实例化类中的成员类型type为...
remove_const去除类型实参中的常量限定
add_const在类型实参上添加常量限定
remove_pointer去除类型实参中的指针属性
add_pointer在类型实参上添加指针属性
remove_reference去除类型实参中的引用属性
add_lvalue_reference在类型实参上添加左值引用
add_rvalue_reference在类型实参上添加右值引用
remove_extent去除数组型类型实参的顶层维度
remove_all_extents去除数组型类型实参的所有维度
decay衰减类型实参:去除CV限定、去除引用属性、数组变指针、函数变函数指针
common_type所有类型实参都可被隐式转换到的公共类型
············
10.3.1.3.2 去除模板类型参数中的引用属性

根据模板的类型参数创建对象时,需要去除其引用属性。

10.3.1.3.3 在模板类型参数上添加引用属性

从模板中返回类型参数对象的引用时,需要为其添加引用属性。

10.3.1.3.4 去除模板类型参数中的常量限定
10.3.1.3.5 将函数类型衰减为函数指针类型

函数类型不能直接声明变量。要想用它声明变量,以便后续调用,就需要将其升级为函数对象类型,或降级为函数指针类型。

10.3.1.4 根据条件使能类型

template<bool B, typename T = void> struct enable_if;

传递给模板布尔型形参B的值为...模板实例化类中的成员类型type为...
true传递给模板类型形参T的类型实参,缺省为void
false无效,引发编译错误
10.3.1.4.1 enable_if的基本用法
10.3.1.4.2 基于enable_if的函数重载

一般而言,(模板)函数间的重载,需要有差异化的参数表,而enable_if则打破了这一限制。

10.3.1.5 根据条件选择类型

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

传递给模板布尔型形参B的值为...模板实例化类中的成员类型type为...
true传递给模板第一个类型形参T的类型实参
false传递给模板第二个类型形参F的类型实参

10.3.1.6 获取返回值的类型

10.3.1.6.1 借助decltype获取返回值的类型
10.3.1.6.2 借助declval和decltype获取返回值的类型
10.3.1.6.3 借助result_of获取返回值的类型

假设Fun为某种可调用对象的类型,Arg1,Arg2,...,为传递给该可调用对象的各个参数的类型,则类模板result_of用Fun(Arg1,Arg2,...)实例化所得类中的成员类型type,即为该可调用对象返回值的类型。

10.3.1.6.4 result_of的基本用法
10.3.1.6.5 result_of的应用案例

10.3.2 变参模板

10.3.2.1 函数模板

10.3.2.1.1 带有可变参数表的函数模板

在C++98/03中,函数模板只能带有固定数量的模板参数,C++11放开了这一限制,允许为函数模板声明0到任意多个模板参数,其形式就是在typename或class关键字后面写上省略号“...”。形如:

10.3.2.1.2 借助递归调用展开参数包

借助递归调用展开带有可变参数表的函数模板的参数包,需要提供一个参数包展开函数,和一个递归终止函数,后者是用来终止递归的。

借助泛型元组和类型萃取也可以达到同样的效果。

10.3.2.1.3 借助逗号表达式和列表初始化展开参数包

所谓逗号表达式,就是形如“表达式1, 表达式2, 表达式3 ... 表达式n”的表达式。逗号表达式的求值过程是,从左到右依次计算每个表达式的值,以最后一个表达式的值作为整个逗号表达式的值。

也可以用初始化列表代替数组。

甚至可以用lambda表达式代替有名函数bar。

10.3.2.2 类模板

10.3.2.2.1 带有可变参数表的类模板

在C++98/03中,类模板只能带有固定数量的模板参数,C++11放开了这一限制,允许为类模板声明0到任意多个模板参数,其形式就是在typename或class关键字后面写上省略号“...”。形如:

10.3.2.2.2 借助模板特化展开参数包

借助模板特化展开带有可变参数表的类模板的参数包,需要提供一个接受任意多个模板参数的通用版本、一个接受至少一个模板参数的特化版本,和一个不接受任何模板参数的特化版本。

使用继承语法也可以达到同样的效果。

10.3.2.2.3 借助泛型元组和索引生成器展开参数包

10.3.2.3 通用打印函数

10.3.2.3.1 C++98/03中的通用打印函数
10.3.2.3.2 C++11中的通用打印函数

10.3.2.4 通用工厂

10.3.2.4.1 C++98/03中的通用工厂
10.3.2.4.2 C++11中的通用工厂

10.3.3 综合应用

10.4 杜绝泄漏

10.4.1 共享指针

10.4.2 独占指针

10.4.3 虚弱指针

10.4.4 第三方库

10.5 简化线程

10.5.1 线程模型

10.5.2 互斥量

10.5.3 条件变量

10.5.4 原子变量

10.5.5 只调一次

10.5.6 异步操作

10.5.7 异步任务

10.6 实用工具

10.6.1 日期时间

10.6.2 数串互转

10.6.3 宽窄字符

10.7 其它特性

10.7.1 委托构造

10.7.2 继承构造

10.7.3 原始字符

10.7.4 阻断继承

10.7.5 显式覆盖

10.7.6 内存对齐

10.7.7 泛型算法