本文继续介绍C++14新特性,Lambda初始化捕获和decltype(auto)。
文章目录
- [第一章 C++14语言特性](#第一章 C++14语言特性)
-
- [1.3 Lambda初始化捕获](#1.3 Lambda初始化捕获)
-
- [1.3.1 Lambda捕获列表语法](#1.3.1 Lambda捕获列表语法)
- [1.3.2 举例](#1.3.2 举例)
- [1.3.3 总结](#1.3.3 总结)
- [1.4 decltype(auto)](#1.4 decltype(auto))
第一章 C++14语言特性
1.3 Lambda初始化捕获
在C++11中,使用Lambda表达式捕获外部变量时,只能捕获左值、拷贝或引用返回方式捕获外部变量。但是,如果在Lambda表达式中访问一个"只可移动,不可拷贝"的对象时,就犯难了。
假如传递一个 std::unique_ptr指针,如果按值传递,编译报错,因为智能指针禁止拷贝;如果按照引用传递,假如这个Lambda在一个线程中处理,处理时间较长,在外边释放后,Lambda表达式中的智能指针出现了悬空引用,导致程序崩溃。
C++11可以在Lambda表达式中使用引用转移资源,但是又需要定义一个变量,这不直接,C++14可以完美解决这个问题。
1.3.1 Lambda捕获列表语法
为了解决这样的问题,C++14引入了捕获列表,可以直接在[ ] 中定义新变量,并进行初始化。
语法格式:
cpp
[ 变量名 = 表达式 ] (参数列表) { ... }
这个"变量名"就是在Lambda内部可以使用的变量的名字。
1.3.2 举例
示例1:通过初始化捕获,将外部资源move到lambda中。
cpp
void test()
{
auto ptr = std::make_unique<int>(42);
// C++14,使用初始化捕获将 ptr 移动到 lambda 内部
auto lambda = [p = std::move(ptr)]()
{
std::cout << "Value: " << *p << std::endl;
};
lambda(); // 输出: Value: 42
// 查看 ptr 是否为空,证明所有权已转移
if (!ptr)
{
std::cout << "ptr is null after move." << std::endl;
}
// ptr is null after move.
}
示例2:直接初始化新变量
cpp
void test()
{
auto generator = [index = 22]()
{
cout << "Index: " << index << endl;
};
generator(); // 输出: Index: 22
}
示例3:避免不必要的拷贝
cpp
std::vector<int> huge_data(10000);
// C++11: [huge_data] 会导致一次昂贵的拷贝
// C++14: [data = std::move(huge_data)] 零拷贝,直接移动
auto process = [data = std::move(huge_data)]() {
std::cout << "Data size: " << data.size() << std::endl;
};
1.3.3 总结
总结C++14的初始化捕获,它就是对C++11Lambda功能上的扩充,是一次"升级",这使得Lambda更好用了。
1.4 decltype(auto)
C++11中的auto可以推导数据类型,decltype擅长推导类型时,不丢失const和引用属性。
C++14中引入了decltype(auto),将这两种推导方式合二为一。
众所周知,auto会丢失引用和const属性,比如:
cpp
int x = 10;
int& ref = x;
auto a = ref; // a 是 int,不是 int&!发生了拷贝。
C++14中,可以使用 decltype(auto)方式,避免丢失引用。
cpp
int x = 10;
int& y = x;
// auto: 忽略引用
auto a = y; // a 是 int
// decltype(auto): 保留引用
decltype(auto) b = y; // b 是 int&
// 【注意】括号的魔法
// (x) 是一个表达式,decltype((x)) 是 int&
decltype(auto) c = (x); // c 是 int&,绑定到 x
例子2:在泛型编程中,实现函数返回值完美转发。
cpp
int g_val = 10;
// 情况 A: 普通 auto
// 丢失引用,返回的是 g_val 的拷贝
auto get_val_copy() { return g_val; }
// 情况 B: auto&
// 强制返回引用,但如果函数里 return 1; 就会报错(不能绑定到右值)
auto& get_val_ref() { return g_val; }
// 情况 C: decltype(auto)
// 完美!如果表达式是左值,就推导为引用;如果是右值,就推导为值。
decltype(auto) get_val_perfect() {
return g_val; // 推导为 int&,因为 g_val 是左值
}
decltype(auto) get_num_perfect() {
return 100; // 推导为 int,因为 100 是右值
}