c++新特性之 左右值 lambda 以及“for”
1.左值和右值
左值定义:左值是一个表达式,它表示一个占有特定内存位置的对象,并且这个对象具有可被程序访问的地址。简单来说,左值是一个可以出现在赋值语句左边的表达式,因为它代表了一个可以被
右值定义:右值是一个表达式,它代表一个临时的值,这个值不具有可被程序访问的地址,通常出现在赋值语句的右边。简单来说,右值是一个只能出现在赋值运算符右边的表达式,它不能作为赋值的目标
右值引用的定义与语法
右值引用的语法形式为T&&,其中T是数据类型。例如,int&&表示对int类型的右值引用,std::string&&表示对std::string类型的右值引用。右值引用只能绑定到右值,不能绑定到左值
左值引用
左值引用是对左值的引用,它为一个已存在的对象提供了一个别名。通过左值引用,可以使用不同的名称来访问同一个对象。左值引用使用
int& ref = num
1.新特性
std::move:函数用于将一个左值强制转换为右值引用,从而可以触发移动语义。它本质上是一个类型转换,将对象标记为可以被移动的。
#include <iostream>
#include <utility>
#include <vector>
int main() {
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1); // 强制移动
std::cout << "vec1 size: " << vec1.size() << std::endl;
std::cout << "vec2 size: " << vec2.size() << std::endl;
return 0;
}
/*vec1 size: 0
vec2 size: 3*/
2.完美转发
完美转发允许函数模板将其参数以原始的左右值属性传递给其他函数,避免不必要的拷贝和类型转换。它结合了右值引用和 std::forward
函数来实现。
#include <iostream>
#include <utility>
void print(int& value) {
std::cout << "Lvalue: " << value << std::endl;
}
void print(int&& value) {
std::cout << "Rvalue: " << value << std::endl;
}
template<typename T>
void forwarder(T&& arg) {
print(std::forward<T>(arg));
}
int main() {
int x = 10;
forwarder(x); // 传递左值
forwarder(20); // 传递右值
return 0;
}
在上述代码中,forwarder
是一个函数模板,它接受一个通用引用 T&&
。std::forward<T>(arg)
会根据 T
的推导结果,将 arg
以原始的左右值属性转发给 print
函数。当传递左值 x
时,调用 print(int&)
;当传递右值 20
时,调用 print(int&&)
。
2.lambda公式
内联函数是一种建议编译器在调用点直接展开函数体而不是进行常规函数调用的机制,这样可以减少函数调用的开销,提高程序的执行效率。Lambda 表达式同样具有这种特性,编译器往往会将其代码在调用处直接展开。
在c++ 内存管理系统之智能指针-CSDN博客,我们知道函数调用是存在在栈,所以会浪费时间
int x=[capture list] (parameter list) -> return type { function body }
capture list 是捕获列表,用于指定 Lambda表达式可以访问的外部变量,以及是按值还是按引用的方式访问。捕获列表可以为空,表示不访问任何外部变量,也可以使用默认捕获模式 & 或 = 来表示按引用或按值捕获所有外部变量,还可以混合使用具体的变量名和默认捕获模式来指定不同的捕获方式。
parameter list 是参数列表,用于表示 Lambda表达式的参数,可以为空,表示没有参数,也可以和普通函数一样指定参数的类型和名称,还可以在 c++14 中使用 auto 关键字来实现泛型参数。
return type 是返回值类型,用于指定 Lambda表达式的返回值类型,可以省略,表示由编译器根据函数体推导,也可以使用 -> 符号显式指定,还可以在 c++14 中使用 auto 关键字来实现泛型返回值。
function body 是函数体,用于表示 Lambda表达式的具体逻辑,可以是一条语句,也可以是多条语句,还可以在 c++14 中使用 constexpr 来实现编译期计算
值捕获
#include <iostream>
using namespace std;
int main() {
int num = 10;
auto lambda = [num]() {
num = 20; // 修改的是捕获的副本
cout << "lambda 内部 num: " << num << endl;
};
lambda();
cout << "外部 num: " << num << endl;
return 0;
}
引用捕获
引用捕获是指 lambda 表达式捕获外部变量的引用,这样在 lambda 表达式内部对变量的操作实际上就是对外部原始变量的操作,修改会反映到外部作用域中。
#include <iostream>
using namespace std;
int main() {
int num = 10;
auto lambda = [&num]() {
num = 20; // 修改的是外部的原始变量
cout << "lambda 内部 num: " << num << endl;
};
lambda();
cout << "外部 num: " << num << endl;
return 0;
}
初始化捕获(C++14 引入)
初始化捕获允许在 lambda 表达式中使用初始化器来初始化一个新的捕获变量,并且可以将表达式的结果绑定到这个新变量上。这为 lambda 表达式提供了更灵活的变量捕获方式,可以处理一些复杂的情况,比如捕获临时对象或对变量进行计算后再捕获。
#include <iostream>
#include <string>
using namespace std;
int main() {
int a = 5;
int b = 3;
// 初始化捕获,将 a + b 的结果捕获为新变量 sum
auto lambda = [sum = a + b]() {
cout << "sum: " << sum << endl;
};
lambda();
return 0;
}
显式捕获
显式捕获是指在 lambda 表达式的捕获列表中明确指定要捕获的变量,包括值捕获和引用捕获。与之相对的是隐式捕获(如 [=]
表示值捕获所有外部可见变量,[&]
表示引用捕获所有外部可见变量 )。显式捕获能让代码更加清晰,明确知道 lambda 表达式依赖哪些外部变量。
#include <iostream>
using namespace std;
int main() {
int x = 10;
int y = 20;
// 显式值捕获 x,显式引用捕获 y
auto lambda = [x, &y]() {
x = 30; // 修改的是 x 的副本
y = 40; // 修改的是外部的 y
cout << "lambda 内部 x: " << x << endl;
cout << "lambda 内部 y: " << y << endl;
};
lambda();
cout << "外部 x: " << x << endl;
cout << "外部 y: " << y << endl;
return 0;
}
引用隐式捕获
使用 [&]
来表示引用隐式捕获,它会以引用的方式捕获 lambda 表达式所在作用域中所有可见的变量。这意味着 lambda 表达式内部对这些变量的修改会直接影响到外部的原始变量。
#include <iostream>
int main() {
int a = 10;
int b = 20;
// 使用 [&] 进行引用隐式捕获
auto lambda = [&]() {
a = 100;
b = 200;
std::cout << "Inside lambda: a = " << a << ", b = " << b << std::endl;
};
lambda();
std::cout << "Outside lambda: a = " << a << ", b = " << b << std::endl;
return 0;
}
3.新for循环
for (declaration : range) {
// 循环体
}
declaration
:用于声明一个变量,这个变量会在每次循环迭代时被初始化为range
中的一个元素。range
:表示一个序列,它可以是数组、std::vector
、std::list
、std::string
等任何定义了begin()
和end()
成员函数或者可以使用std::begin()
和std::end()
函数的对象
#include <iostream>
#include <vector>
int main() {
// 定义一个 std::vector
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用范围 for 循环遍历 vector
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 也可以用于遍历数组
int arr[] = {6, 7, 8, 9, 10};
for (int val : arr) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}