C++ 函数参数按值、左值引用、右值引用传递
对于按值传递(pass-by-value)的函数:
- 当传入右值(rvalue)时,会优先调用移动构造函数(move constructor)。这是因为右值通常是临时对象,其资源可以安全地被“窃取”而无需进行深拷贝,从而提高效率。 移动构造函数正是为此目的而设计的。
- 当传入左值(lvalue)时,会调用拷贝构造函数(copy constructor)。因为左值通常具有名称,并且在函数调用后可能仍然需要使用,所以需要创建一个副本以确保原始对象的状态不受函数内部操作的影响。
简而言之:
- 右值 → 移动构造
- 左值 → 拷贝构造
值得注意的是,如果一个类没有定义移动构造函数,但定义了拷贝构造函数,那么即使传入右值,编译器也会退而求其次调用拷贝构造函数。
- 参数为非常量左值引用的函数(T&)仅接受左值。
- 正确。 非常量左值引用旨在修改传入的对象,而右值(通常是临时对象或字面量)是不可修改的(或者修改它们没有意义,因为它们很快就会消失)。因此,将右值绑定到非常量左值引用是被禁止的。
- 例如:
void func(int& x) {
x = 10;
}
int main() {
int a = 5;
func(a); // 正确,a是左值
// func(5); // 错误!5是右值,不能绑定到 int&
return 0;
}
- 参数为常量左值引用的函数(const T&)接受左值和右值。
- 正确。 这是C++中一个非常重要的特性,被称为“常量引用延长临时对象的生命周期”。
- 当传入左值时,常量左值引用会像普通引用一样绑定到该左值,但不能通过此引用修改对象。
- 当传入右值时,编译器会创建一个临时对象来存储该右值,然后将常量左值引用绑定到这个临时对象。这个临时对象的生命周期会被延长到持有其引用的常量左值引用的生命周期结束。这使得我们可以安全地将临时对象传递给函数,而无需进行不必要的拷贝。
- 例如:
- 正确。 这是C++中一个非常重要的特性,被称为“常量引用延长临时对象的生命周期”。
void func_const_ref(const int& x) {
// x = 10; // 错误!不能通过常量引用修改
std::cout << x << std::endl;
}
int main() {
int a = 5;
func_const_ref(a); // 正确,a是左值
func_const_ref(5); // 正确,5是右值,会创建一个临时对象
return 0;
}
- 参数为右值引用的函数(T&&)仅接受右值。
- 正确。 右值引用专门设计用来绑定到右值,以实现移动语义和完美转发等。它们不能直接绑定到左值。
- 如果确实需要将一个左值当作右值来处理(例如,你知道这个左值后续不再使用,可以安全地移动其资源),你需要使用
std::move将其显式转换为右值。 - 例如:
void func_rvalue_ref(int&& x) {
std::cout << x << std::endl;
// 通常在这里会进行资源移动操作,例如:
// int new_val = std::move(x);
}
int main() {
int a = 5;
// func_rvalue_ref(a); // 错误!a是左值,不能绑定到 int&&
func_rvalue_ref(5); // 正确,5是右值
func_rvalue_ref(std::move(a)); // 正确,std::move(a) 将 a 转换为右值
return 0;
}