[整理] C++11/14 在 OI 方面的新特性
本文中特性按照主观的重要程度进行排列。
欢迎指正、补充。
### auto
在 C++11 中,它可以通过**赋值**来推断变量的类型。
使用例:
```cpp
auto a = 114514;
auto b = "I AK IOI";
```
执行后,变量 `a` 就会自动被分配为整数型, `b` 为 `char` 指针(即 `char` 字符串)型。

请注意,`auto` 不能定义数组,也不能作为函数的参数。
而我认为,它最大的节省写码时间的地方在于可以很方便的写出迭代器。
例如,我们对 `vector>>>` 写一个迭代器。
在 C++11 之前需要这么写:
```cpp
vector > > > v;
vector > > >::iterator iter = ...;
```
但是使用 auto,我们可以很方便地解决这个问题。
```cpp
vector>>> v;
auto iter = v.begin();
```
它会通过 `v.begin()` 自动推导出迭代器的类型。
在 C++14 中,函数返回类型也可用 `auto` 了。
使用例:
```cpp
auto IAKIOI()
{
return 114514 + 1919810;
}
```

### 新的 for 结构
C++11 中新增加了一种 `for` 的结构,如下所示:
```cpp
for (name x : container)
{
...
}
```
他会遍历 `container` 这一个容器,遍历的值放到类型为 `name` 的变量 `x` 上。
这同样是一个能够减小码量节省时间的办法。
同样,它也可以使用 `auto`。
使用例:
```cpp
vector v;
...
for (auto x : v)
{
...
}
```
请注意,如果需要通过这种方式进行对容器内元素的修改,需要定义引用形式的变量。
```cpp
vector v;
...
for (auto &x : v)
x += 114514;
```
### 对模板中连续两个右尖括号 (>>) 的优化
C++11 中新增。
Dijkstra 堆优化中常见有一种结构。
在 C++11 以前我们需要这么写。
```cpp
priority_queue > q;
```
`pair` 的右括号和 `priority_queue` 的之间必须隔开一个空格。
现在我们可以不用这个空格了。
```cpp
priority_queue> q;
```
### function & bind
C++11 中新增。
`function` 是可调用对象的封装器,可以把 `function` 看做一个函数对象,用于表示函数这个抽象概念。
`function` 的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为`function` 的目标。
可调用对象可以是:
- 一个函数指针
- 一个具有 `operator()` 成员函数的类对象(传说中的仿函数),`lambda`表达式
- 一个可被转换为函数指针的类对象
- 一个类成员(函数)指针
- bind 表达式或其它函数对象
使用例 (来自 [https://zh.cppreference.com/w/cpp/utility/functional/function](https://zh.cppreference.com/w/cpp/utility/functional/function)):
```cpp
#include
#include
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << '\n';
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// 存储自由函数
std::function f_display = print_num;
f_display(-9);
// 存储 lambda
std::function f_display_42 = []() { print_num(42); };
f_display_42();
// 存储到 std::bind 调用的结果
std::function f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// 存储到成员函数的调用
std::function f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
f_add_display(314159, 1);
// 存储到数据成员访问器的调用
std::function f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// 存储到成员函数及对象的调用
using std::placeholders::_1;
std::function f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);
// 存储到成员函数和对象指针的调用
std::function f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);
// 存储到函数对象的调用
std::function f_display_obj = PrintNum();
f_display_obj(18);
auto factorial = [](int n) {
// 存储 lambda 对象以模拟“递归 lambda ”,注意额外开销
std::function fac = [&](int n){ return (n < 2) ? 1 : n*fac(n-1); };
// note that "auto fac = [&](int n){...};" does not work in recursive calls
return fac(n);
};
for (int i{5}; i != 8; ++i) { std::cout << i << "! = " << factorial(i) << "; "; }
}
```
`bind` 可以让可调用对象和参数绑定,其结果使用 `function` 保存。
其可以:
- 将可调用对象与参数一起绑定为另一个 `function` 供调用
- 将 $n$ 元可调用对象转成 $m(m < n)$ 元可调用对象,绑定一部分参数,这里需要使用 `placeholders`
使用例:
```cpp
#include
#include
#include
#include
void f(int n1, int n2, int n3, const int& n4, int n5)
{
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
int g(int n1)
{
return n1;
}
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
int main()
{
using namespace std::placeholders; // 对于 _1, _2, _3...
// 演示参数重排序和按引用传递
int n = 7;
// ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数)
auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 为 _1 所绑定, 2 为 _2 所绑定,不使用 1001
// 进行到 f(2, 42, 1, n, 7) 的调用
// 嵌套 bind 子表达式共享占位符
auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
f2(10, 11, 12); // 进行到 f(12, g(12), 12, 4, 5); 的调用
// 常见使用情况:以分布绑定 RNG
std::default_random_engine e;
std::uniform_int_distribution<> d(0, 10);
std::function rnd = std::bind(d, e); // e 的一个副本存储于 rnd
for(int n=0; n<10; ++n)
std::cout << rnd() << ' ';
std::cout << '\n';
// 绑定指向成员函数指针
Foo foo;
auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
f3(5);
// 绑定指向数据成员指针
auto f4 = std::bind(&Foo::data, _1);
std::cout << f4(foo) << '\n';
// 智能指针亦能用于调用被引用对象的成员
std::cout << f4(std::make_shared(foo)) << '\n'
<< f4(std::make_unique(foo)) << '\n';
}
```
### lambda 匿名函数
C++11 中新增。
由于内容繁多,在此不予过多介绍。
请参见:[https://docs.microsoft.com/zh-cn/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160](https://docs.microsoft.com/zh-cn/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160)
在 C++14 中,允许使用 `auto`。
### 随机数
C++11 更新了 `` 库, 对随机数生成进行了优化,这里进行部分介绍。
- 均匀分布的 `unsigned` 值
使用例:
```cpp
int min, max;
// 定义上下边界
default_random_engine e;
// 创建引擎
uniform_int_distribution u(min, max);
// 创建取值范围
int randNum = u(e);
// 获取伪随机数
```
- 生成随机实数
使用例:
```cpp
default_random_engine e;
// 生成无符号随机整数
// 0 到 1(包含)的均匀分布
uniform_real_distributionu(0, 1);
for (int i = 0;i < 10; i++)
double u = u(e);
```
- `mt19937`
使用例:
```cpp
std::mt19937 mt_rand(std::random_device{}());
std::mt19937 mt_rand(time(0));
std::mt19937 mt_rand(std::chrono::system_clock::now().time_since_epoch().count());
```
请参见 [https://zh.cppreference.com/w/cpp/header/random](https://zh.cppreference.com/w/cpp/header/random)
### ``
C++11 新增。
由于内容繁多,在此不予过多介绍。
请参见:[https://zh.cppreference.com/w/cpp/header/chrono](https://zh.cppreference.com/w/cpp/header/chrono)
### ``
C++11 新增,这是一种新的数组定义方式。
它提供给了数组更多高级功能,包括但不限于迭代器访问、获取容量、获得原始指针等。
我们可以通过以下等等方式来定义一个 `array` :
```cpp
std::array a0 = {0, 1, 2, 3, 4};
std::array a1 = a0;
std::array a2;
```
它支持 `[]`, `at`, `front`, `back`, `data` 访问元素,也支持迭代器进行访问。
```
at: 访问指定元素并进行越界检查
[]: 访问指定元素
front[]: 访问第一个元素
back[]: 访问最后一个元素
data: 返回指向内存中数组第一个元素的指针
begin: 返回第一个元素的迭代器
end: 尾端迭代器
rbegin, rend: 逆向迭代器
```
也支持其他函数:
```
empty: 检查容器是否为空
size: 返回容纳的元素数
max_size: 返回可容纳的最大元素数
fill: 以指定值填充容器
swap: 交换内容
```
使用例 (来自 [https://blog.csdn.net/qq_38410730/article/details/102802239](https://blog.csdn.net/qq_38410730/article/details/102802239)):
```cpp
#include
#include
#include
int main()
{
std::array a1 = {4, 0, 2, 1, 3};
std::array a2;
std::sort(a1.begin(), a1.end()); //排序函数
for(int a: a1)
std::cout << a << ' ';
std::cout << std::endl;
std::reverse(a1.begin(), a1.end()); //反转a1
for (std::array::iterator iter = a1.begin(); iter != a1.end(); ++iter)
std::cout << *iter << " ";
std::cout << std::endl;
std::reverse_copy(a1.begin(), a1.end(), a2.begin()); //反转a1的内容拷贝到a2
for (int i = 0; i < a2.size(); ++i)
std::cout << a2[i] << " ";
std::cout << std::endl;
}
```
```cpp
# include
# include
int main(int argc, char const *argv[])
{
std::array a = {0, 1, 2, 3, 4};
std::cout << a.front() << " " << a.at(1) << " " << a[2] << " " << *(a.data() + 3) << " " << a.back() << std::endl;
std::array::iterator iter;
for (iter = a.begin(); iter != a.end(); ++iter)
std::cout << *iter << " ";
std::cout << std::endl;
std::array::reverse_iterator riter;
for (riter = a.rbegin(); riter != a.rend(); ++riter)
std::cout << *riter << " ";
std::cout << std::endl;
return 0;
}
```
### 二进制字面量
顾名思义,使用例:
```cpp
int a = 0b0001'0011'1010;
```
### 支持函数 template 的默认参数
C++11 新增,在 OI 中用于快读中区分 `int` 和 `long long`。
使用例 (来自 [https://zh.cppreference.com/w/cpp/utility/functional/bind](https://zh.cppreference.com/w/cpp/utility/functional/bind)):
```cpp
template
inline T read()
{
...
}
```
如果我们需要对 `int` 快读,那就直接
```cpp
int t = read();
```
对于 `long long`,则可以区别对待;
```cpp
long long t = read();
```
### 支持在函数 template 中使用可变参数
C++11 新增,在 OI 中用于读入或输出多个变量。
使用例:
```cpp
int read()
{
int fx = 1, xx = 0;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
fx = -1;
ch = getchar();
}
while (isdigit(ch))
xx *= 10, xx += ch - 48, ch = getchar();
return fx * xx;
}
void Read()
{
return;
}
template
void Read(FirstArgv& argv, Args&... args)
{
argv = read();
Read(args...);
}
```

### 新增的一些数据结构和算法
#### 数据结构:
- `tuple` 使用例:[https://paste.ubuntu.com/p/RxsG9JTVQX/](https://paste.ubuntu.com/p/RxsG9JTVQX/)
- `forward_list`
- `unordered_set`
- `unordered_map`
- `array`
#### 算法:
- `all_of, any_of, none_of`: 检测表达式对范围内的元素是否 全部为真/至少有一个为真/都不是真。
使用例:
```cpp
std::vector v;
...
if (all_of(v.cbegin(), v.cend(), [](int i) { return !(i & 1); }))
cout << "所有数字都是偶数";
if (any_of(v.cbegin(), v.cend(), [](int i) { return !(i & 1); }))
cout << "至少有一个数字是偶数";
if (none_of(v.cbegin(), v.cend(), [](int i) { return !(i & 1); }))
cout << "没有数字是偶数";
```
- `minmax_element`: 找到容器中最大元素最小元素的位置。
使用例:
```cpp
int main()
{
std::vector v = {4, 5, 1, 9, 8, 0};
auto result = std::minmax_element(v.begin(), v.end());
std::cout << "最小元素是: " << *(result.first) << endl;
std::cout << "最大元素是: " << *(result.second) << endl;
return 0;
}
```

### 使用 using 代替 typedef
基本在开 `long long` 的时候使用。
使用例:
```cpp
typedef long long ll;
using ll = long long;
```
二者等效。
### decltype
> decltype 是 "declare type" 的缩写。
C++11 新增,它的作用是可以通过**表达式**来推断变量的类型。
使用例:
```cpp
decltype(1919 * 810) a = 1;
decltype(114.514) b = 14.7;
int x = 0;
decltype(x) c = 2;
```

### 返回类型后置
这是 C++11 中新增的一种语法,可以使用 `decltype` 和 `auto` 结合在一起完成对返回值的推导,但是在 OI 中使用不多。
使用例:
```cpp
template
auto func(T t, U u) -> decltype(t + u)
{
return t + u;
}
```
参考文档:
[https://zh.cppreference.com/w/cpp](https://zh.cppreference.com/w/cpp)
[https://blog.csdn.net/qq_38410730/article/details/102802239](https://blog.csdn.net/qq_38410730/article/details/102802239)
[https://www.jianshu.com/p/6d9a7de995bb](https://www.jianshu.com/p/6d9a7de995bb)
[https://www.cnblogs.com/jwk000/p/3560086.html](https://www.cnblogs.com/jwk000/p/3560086.html)
[https://blog.csdn.net/tsbyj/article/details/46994851](https://blog.csdn.net/tsbyj/article/details/46994851)
[https://zhuanlan.zhihu.com/p/137884434](https://zhuanlan.zhihu.com/p/137884434)
[https://zhuanlan.zhihu.com/p/139515439](https://zhuanlan.zhihu.com/p/139515439)
[http://c.biancheng.net/view/8600.html](http://c.biancheng.net/view/8600.html)
[https://blog.csdn.net/aced96/article/details/118018623](https://blog.csdn.net/aced96/article/details/118018623)
[http://c.biancheng.net/view/3730.html](http://c.biancheng.net/view/3730.html)
[http://c.biancheng.net/view/3734.html](http://c.biancheng.net/view/3734.html)
[http://c.biancheng.net/view/3727.html](http://c.biancheng.net/view/3727.html)
[http://c.biancheng.net/view/7151.html](http://c.biancheng.net/view/7151.html)
[https://docs.microsoft.com/zh-cn/cpp/cpp/ellipses-and-variadic-templates?view=msvc-160](https://docs.microsoft.com/zh-cn/cpp/cpp/ellipses-and-variadic-templates?view=msvc-160)