
运行时增强

语言运行期的强化
Lambda表达式
提供类似匿名函数的特性
基础
concept
1 | [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { |
值捕获
与参数传值类似,值捕获的前提是变量可以拷贝,不同之处在于被捕获的变量在Lambda表达式被创建时拷贝,而非调用时拷贝
:unamused: example
1
2
3
4
5int value = 1;
auto copy_value = [value]() -> int {
// [value] {} 省略写法
return value;
}引用捕获
与引用传参类似,引用捕获保存的是引用,值会发生变化
example
1
2
3
4
5
6int value = 1;
auto copy_value = [&value]() -> int {
return value;
}
value = 100;
copy_value(); // 100隐式捕获
- []空捕获列表
- [name1, name2, …]捕获一系列变量
- [&]引用捕获,让编译器自动推导引用列表
- [=]值捕获,让编译器自行推导值捕获列表
表达式捕获
上面提到的值捕获、引用捕获都是在外层作用域声明的变量,因此这些捕获方式捕获的都是左值,而不是捕获右值
C++14允许捕获的成员用任意的表达式进行初始化(也就允许了右值捕获)
example
1
2
3
4auto important = std::make_unqiue<int>(1);
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
return x + y + v1 + (*v2);
}important是一个是一个独占指针,是不能够被
=
捕获到,可以将其转移为右值
泛型lambda
auto
关键字是不能用在参数列表中的(C++20起可通过概念和约束实现),因为这种写法会与模板的功能有冲突
但Lambda表达式并非普通函数,在没有明确指名参数表类型的情况下,Lambda表达式并不能进行模板化
C++14起,Lambda函数的形式参数可以通过auto
来产生意义上的泛型
example
1 | auto add = [](auto x, auto y){ |
函数对象包装器
std::function
Lambda表达式的本质是一个和函数对象类似的类类型(闭包类型)的对象(闭包对象)
tips: 当Lambda表达式的捕获列表为空时,闭包对象还能够转换为函数指针值进行传递
C++11起,将能够被调用的对象的类型,统一称为可调用类型(
std::function
)是一种通用、多态的函数封装,其实例可以对任何可以调用的目标实体进行存储、复制和调用类型(也是对C++现有的可调用实体的一种类型安全的包裹/函数指针的调用不是类型安全的)
example
1 | std::function<int(int)> func = foo; // 返回值和参数都为int类型 |
std::bind & std::placeholder
std::bind:用来绑定函数调用参数的(有时并不能够一次性获取调用某个函数的全部参数)
通过这个函数,可以将函数调用部分参数提前绑定到函数身上成为一个新的对下稿,然后在参数齐全后完成调用
example
1 | int foo(int a, int b, int c) {} |
右值引用
左值、右值的纯右值、将亡值、右值
左值:表达式结束后依然存在的持久对象
右值:表达式结束后不再存在的临时对象
纯右值:纯粹的字面量,求值结果相当于字面量或匿名临时对象,Lambda表达式
tips: 字面量除了字符串字面量之外均为纯右值,而字符串字面量是一个左值,类型为const char
数组
将亡值:即将被销毁,却能够移动的值
1
2
3
4
5std::vector<int> foo() {
std::vector<int> temp = {1, 2, 3, 4};
return temp;
}
std::vector<int> v = foo();
左值引用和右值引用
要拿到一个将亡值,就需要用到右值引用: T &&,其中T是类型;右值引用的声明让这个临时值的声明周期得以延长、只要变量活着,那么将亡值将继续存活
C++11提供了std::move
这个方法将左值参数无条件的转移为右值
example
1 | std::string lv1 = "xxx"; // lv1为左值 |
一个历史遗留问题:
1 | int& a = std::move(1); // invalid, 存在逻辑错误 |
移动语义
传统C++没有区分移动和拷贝的概念,造成大量的数据拷贝,浪费空间和时间
完美转发
一个声明的右值引用其实是一个左值
issue
1 | void reference(int &v) {} |
v是一个左值,为什么可以传递给pass(T&&)呢?
引用坍缩规则
函数形参类型 实参参数类型 推导后函数形参类型 T& 左引用 T& T& 右引用 T& T&& 左引用 T& T&& 右引用 T&&
无论模板参数是什么类型的引用,当且仅当实参类型为右引用时,模板参数才能被推导为右引用类型
为了在传递参数的时候,保持原来的参数类型(左引用保持左引用,右引用保持右引用)使用std::forward
来进行参数传递
example
1 | void reference(int &v) {} |