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

简介

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

可以绑定到左值、右值、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++》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值