新版 C++ 特性
注意:考虑到算法竞赛的实际情况,本文将不会全面研究语法,只会讲述在算法竞赛中可能会应用到的部分。
本文语法参照 C++11 标准。语义不同的将以 C++11 作为标准,C++14、C++17 的语法视情况提及并会特别标注。
auto
类型说明符
auto
类型说明符用于自动推导变量等的类型。例如:
基于范围的 for
循环
下面是 C++20 前 基于范围的 for
循环的语法:
上述语法产生的代码等价于下列代码(__range
、__begin
和 __end
仅用于阐释):
range_declaration 范围声明
范围声明是一个具名变量的声明,其类型是由范围表达式所表示的序列的元素的类型,或该类型的引用。通常用 auto
说明符进行自动类型推导。
range_expression 范围表达式
范围表达式是任何可以表示一个合适的序列(数组,或定义了 begin
和 end
成员函数或自由函数的对象)的表达式,或一个花括号初始化器列表。正因此,我们不应在循环体中修改范围表达式使其任何尚未被遍历到的“迭代器”(包括“尾后迭代器”)非法化。
这里有一个例子:
loop_statement 循环语句
循环语句可以是任何语句,常为一条复合语句,它是循环体。
这里有一个例子:
Lambda 表达式
详见 Lambda 表达式 页面。
decltype 说明符
decltype
说明符可以推断表达式的类型。
constexpr
constexpr
说明符声明可以在编译时求得函数或变量的值。其与 const
的主要区别是一定会在编译时进行初始化。用于对象声明的 constexpr
说明符蕴含 const
,用于函数声明的 constexpr
蕴含 inline
。来看一个例子
在 int fact(int x)
之前加上 constexpr
则编译通过。
std::tuple
std::tuple
定义于头文件 <tuple>
,是固定大小的异类值汇集(在确定初始元素后不能更改,但是初始元素能有任意多个)。它是 std::pair
的推广。来看一个例子:
成员函数
函数 | 作用 |
---|---|
operator= | 赋值一个 tuple 的内容给另一个 |
swap | 交换二个 tuple 的内容 |
例子
非成员函数
函数 | 作用 |
---|---|
make_tuple | 创建一个 tuple 对象,其类型根据各实参类型定义 |
std::get | 元组式访问指定的元素 |
operator== 等 | 按字典顺序比较 tuple 中的值 |
std::swap | 特化的 std::swap 算法 |
例子
std::function
类模板 std::function
是通用多态函数封装器,定义于头文件 <functional>
。std::function
的实例能存储、复制及调用任何可调用(Callable)目标——函数、Lambda 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。
存储的可调用对象被称为 std::function
的 目标。若 std::function
不含目标,则称它为 空。调用空 std::function
的目标将导致抛出 std::bad_function_call
异常。
来看例子
可变参数宏
可变参数宏是 C99 引入的一个特性,C++ 从 C++11 开始支持这一特性。可变参数宏允许宏定义可以拥有可变参数,例如:
其中,...
是缺省符号,__VA_ARGS__
在调用时会替换成实际的参数列表,def_body
应为可变参数模板函数。
现在就可以这么调用 def_name
:
可变参数模板
在 C++11 之前,类模板和函数模板都只能接受固定数目的模板参数。C++11 允许 任意个数、任意类型 的模板参数。
可变参数模板类
例如,下列代码声明的模板类 tuple
的对象可以接受任意个数、任意类型的模板参数作为它的模板形参。
其中,Values
是一个模板参数包,表示 0 个或多个额外的类型参数。模板类只能含有一个模板参数包,且模板参数包必须位于所有模板参数的最右侧。
所以,可以这么声明 tuple
的对象:
如果要限制至少有一个模板参数,可以这么定义模板类 tuple
:
可变参数模板函数
同样的,下列代码声明的模板函数 fun
可以接受任意个数、任意类型的模板参数作为它的模板形参。
其中,Values
是一个模板参数包,values
是一个函数参数包,表示 0 个或多个函数参数。模板函数只能含有一个模板参数包,且模板参数包必须位于所有模板参数的最右侧。
所以,可以这么调用 fun
函数:
参数包展开
之前说面了如何声明模板类或者模板函数,但是具体怎么使用传进来的参数呢?这个时候就需要参数包展开。
对于模板函数而言,参数包展开的方式有递归函数方式展开以及逗号表达式和参数列表方式展开。
对于模板类而言,参数包展开的方式有模板递归方式展开和继承方式展开。
递归函数方式展开参数包
递归函数方式展开参数包需要提供展开参数包的递归函数和参数包展开的终止函数。
举个例子,下面这个代码段使用了递归函数方式展开参数包,实现了可接受大于等于 2 个参数的取最大值函数。
可变参数模板的应用
举个应用的例子,有的人在 debug 的时候可能不喜欢用 IDE 的调试功能,而是喜欢输出中间变量。但是,有时候要输出的中间变量数量有点多,写输出中间变量的代码的时候可能会比较烦躁,这时候就可以用上可变参数模板和可变参数宏。
这样一来,如果事先在代码模板里写好 DEBUG 的相关代码,后续输出中间变量的时候就会方便许多。