C++11中的万能引用和右值引用使用注意事项

本文详细介绍了C++中的右值引用与万能引用的概念及其应用,包括两者的区别及如何正确使用std::forward与std::move。此外,还讨论了在返回值优化(RVO)场景中使用std::move的潜在问题。
简介

右值引用仅会绑定到右值,主要用于识别出可移对象。

可以绑定到左值、右值、const、非const、volatile、非volatile等等一切对象的引用,称为万能引用。

右值引用和万能引用都使用形如“T&&”的符号表示。

下面做个小测试,试着区分一下下面的引用属于万能引用还是右值引用:

// 1.
void f(Foo&& p);

// 2.
Foo&& var1 = Foo();

// 3.
auto&& var2 = var1;

// 4.
template<typename T>
void f(std::vector<T>&& p);

// 5.
template<typename T>
void f(T&& p);
识别万能引用

先说上面测试的结果:3和5是万能引用,其他都是右值引用。

可见,只有涉及类型推导的引用,才会是万能引用。

另外一个条件,就是它的形式必须是“T&&”,上述第4个语句因为不符合该条件,所以只是个右值引用。

基于这个原因,即使是一个const饰词,一个看上去是万能引用的场合可能也只是个右值引用。

对万能引用实施std::forward

首先需要注意一点,对右值引用使用std::move没有任何问题。但对万能引用使用std::move会导致问题。

因为对于万能引用,如果采用右值来初始化,得到的是一个右值引用,如果采用左值来初始化万能引用,那么得到的是一个左值引用。

所以如果对万能引用使用std::move,不管是左值初始化还是右值初始化,那么得到的只能是右值,这可能不是你希望看到的结果。

考虑下面的代码:

class Foo {}
 public:
  template<typename T>
  void SetName(T&& new_name) {
  	name_ = std::forward<T>(new_name);
  }
 //...
 
 private:
 	string name_;
;

说明如下:

  • 在模板内,new_name 是万能引用,赋值时采用万能引用,这样不管是左值调用还是右值调用,都将得到合理的结果
  • 如果把赋值语句修改为:name_ = std::move(new_name); 将会导致以下问题:
    • 当以一个局部变量调用SetNmae时,调用者会合理地假设这是一个对参数的只读操作。但实际上参数已经是一个不确定的值,改变了意图
不要对RVO施加move转换

如果代码:

Foo MakeFoo()
{
	Foo f;
	
	//...
	
	return f;
}

有人可能会觉得,既然使用std::move有机会把复制构造转换为移动构造,那么对返回值使用std::move也可以达到同样的优化目的了。

“优化”后的代码如下:

Foo MakeFoo()
{
	Foo f;
	
	//...
	
	return std::move(f);
}

先结论,这样做是不正确的,它可能并不会达到优化的目的,甚至很可能比上一个代码更糟糕。

原因如下:

  • C++标准已经规定了返回值优化(return value optimization, RVO),通过直接在为函数返回值分配的内存上创建局部变量f来避免复制
  • 上述代码符合RVO条件,编译器会为它作出优化,所以它实际上不会复制对象,效率极高
  • 使用move后,它移动f的内容到MakeFoo的返回值空间,而这比RVO多出一些操作,必然更慢
  • 但有一点需要注意,执行RVO需要同时满足以下条件,缺一不可:
    • 局部对象类型和函数返回值类型相同
    • 返回的就是局部对象本身
  • 即使未满足上述条件,c++标准仍然规定,编译器需要把返回对象作为右值处理

综上,对返回值执行std::move就是多此一举、画蛇添足。

小结

右值引用和万能引用为我们实现期望的功能提供了强大的助力,使用得当可得到如虎添翼的效果。

但也要注意里面涉及的细节,稍不小心,也可能埋下以后加班调试的坑。

参考资料

《Effective Modern C++》

<think>引用C++11引入的重要特性,主要用于实现移动语义完美转发。下面详细介绍引用的概念、使用场景及示例。</think> ### 引用详解 引用C++11引入的核心特性(用`&&`表示),主要解决深拷贝性能问题实现完美转发。以下是详细说明示例: --- #### **核心概念** 1. **左(lvalue)**:有持久存储(变量、对象) 2. **(rvalue)**:临时对象(字面量、表达式结果) 3. **引用**:绑定到引用,延长临时对象生命周期 --- #### **使用场景及示例** **1. 移动语义(避免深拷贝)** ```cpp class Vector { int* data; size_t size; public: // 移动构造函数 Vector(Vector&& other) noexcept : data(other.data), size(other.size) { other.data = nullptr; // 置空源对象 } // 移动赋运算符 Vector& operator=(Vector&& other) noexcept { delete[] data; // 释放现有资源 data = other.data; size = other.size; other.data = nullptr; return *this; } }; int main() { Vector v1(1000); // 常规构造 Vector v2 = std::move(v1); // 移动构造(避免1000次拷贝) } ``` **2. 完美转发(保持类别)** ```cpp // 转发函数模板 template<typename T> void wrapper(T&& arg) { process(std::forward<T>(arg)); // 保持左/属性 } void process(int& x) { std::cout << "lvalue\n"; } void process(int&& x) { std::cout << "rvalue\n"; } int main() { int a = 5; wrapper(a); // 输出 "lvalue" wrapper(10); // 输出 "rvalue" } ``` **3. 返回临时对象的优化** ```cpp // 工厂函数 Matrix createMatrix() { return Matrix(100, 100); // 隐式移动构造 } int main() { Matrix m = createMatrix(); // 无额外拷贝 } ``` **4. 标准库应用(`emplace_back`)** ```cpp std::vector<std::string> vec; vec.emplace_back("Hello"); // 直接构造,避免临时string拷贝 ``` **5. `std::move`强制转换** ```cpp std::string str = "Hello"; std::string str2 = std::move(str); // str内容转移至str2, str变为空 ``` --- #### **关键原理** - **移动语义**:通过"窃取"资源所有权而非复制,极大提升性能 - **引用折叠规则**: - `T& &` → `T&` - `T& &&` → `T&` - `T&& &` → `T&` - `T&& &&` → `T&&` - **万能引用**:`T&&`在模板中可绑定左/ --- #### **注意事项** 1. 被`move`后的对象处于有效但未定义状态 2. 移动构造/赋需加`noexcept`保证异常安全 3. 成员函数引用重载:`obj.func() &` / `obj.func() &&` --- #### **完整示例:智能指针移动** ```cpp class UniquePtr { int* ptr; public: UniquePtr(UniquePtr&& other) : ptr(other.ptr) { other.ptr = nullptr; } ~UniquePtr() { delete ptr; } }; UniquePtr createResource() { return UniquePtr(new int(42)); } int main() { UniquePtr p1 = createResource(); // 移动构造 UniquePtr p2 = std::move(p1); // 显式移动 } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值