r3live ThreadPool (1)右值引用及相关内容

1. 定义:

左值:常规变量;右值:临时变量。
右值引用:右值的引用,符号:&&。
注:右值引用,如果有变量名字,也会看作为左值(这将引出forward的定义)。

all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references)

2. 右引用的好处

  • 问题引出:重复的构造-析构
class Class{};
# case 1
a=Class();
# case 2
void fun(Class c){}
fun(Class());
  • 右引用的好处:
    于是定义移动构造函数、移动赋值函数等,减少右引用情况下,不必要的构造-析构过程。
  • std::move():
    如果我们希望某左值也能使用这个好处,且它在之后不会被用到,则可以调用 std::move将左值转换为右值。

template <class T>
typename remove_reference<T>::type&& move (T&& arg) noexcept;

std::move无论传入左值还是右值,都是返回右值。
注:std::move的定义需配合以下说明来理解:

C++11 standard provides special rules for reference collapsing, which are as follows:
Object & & = Object &
Object & && = Object &
Object && & = Object &
Object && && = Object &&

3. 右引用的问题

使用右引用时,一个问题出现了。有名字的量都会被作为左值;所以如果函数某参数的类型为右引用,该值在函数内部仍会作为左值来传递,这将不利于函数内的传值操作(参考 https://www.cplusplus.com/reference/utility/forward/?kw=forward

为了让右引用值在有名字的情况下仍能被作为右值来传递,官方定义了函数std::forward和相关的规则:

template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;

在这巧妙的设计下,右引用或左引用可以被保持:

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template <class T> void fun (T&& x) {
  overloaded (x);                   // always an lvalue
  overloaded (std::forward<T>(x));  // rvalue if argument is rvalue
}

注1:需要注意这里的T!! T与x是左值还是右值无关。
注2:如果无引用的类型传入&&类型,会被视为&类型。


参考测试代码完整版:

#include <utility>      // std::move
#include <iostream>     // std::cout
#include <vector>       // std::vector
#include <string>       // std::string

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >void forwarding( t && arg ) {
   // 0. no action
   overloaded( arg );
   // 1. move()
   overloaded( std::move( arg ) ); // conceptually this would invalidate arg
   // 2. forward()
   overloaded( std::forward< t >( arg ) );
}

int main () {
   //=============== move() can avoid copy ==================
   std::string foo = "foo-string";
   std::string bar = "bar-string";
   std::vector<std::string> myvector;
   // 0.no action
   myvector.push_back (foo);                    // copies
   // 1.move()
   myvector.push_back (std::move(bar));         // moves
   std::cout<<bar<<std::endl; // no longer exist
   myvector.push_back (std::move("abc-string"));// no action
   // 2.false usage of forward()
   myvector.emplace_back(std::forward<std::string>(foo));// false !!
   std::cout<<foo<<std::endl;//no longer exist

   std::cout << "myvector contains:";
   for (std::string& x:myvector) std::cout << ' ' << x;
   std::cout << '\n';

   //================= check type of return value ==================
   std::cout << std::endl;
   forwarding( 5 );
   std::cout << std::endl;
   int x = 5;
   forwarding( x );

   return 0;
}

参考链接

官方:https://www.cplusplus.com/reference/utility/forward/?kw=forward
官方:https://www.cplusplus.com/reference/utility/move/
清晰的说明:https://stackoverflow.com/questions/7510182/how-does-stdmove-transfer-values-into-rvalues

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 右值引用是个效率工具,减少拷贝。C++11标准提出的一类数据类型。用于实现转移语义(move semantic)与...
    book_02阅读 104评论 0 1
  • 本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用,不得用于任何商业用途。 左...
    深红的眼眸阅读 11,349评论 1 12
  • 右值引用是cpp11引入的很重要的特性,是为了支持一下两个特性而引入的: 去除不必要的对象内存拷贝,极大提高了程序...
    Jesonwoo阅读 724评论 0 0
  • 在std::promise范例中,使用了std::ref将future对象传递给引用参数类型的任务函数。 std:...
    hijiang阅读 1,080评论 0 0
  • 首先,本章很长,也较难理解,建议读者有大段连续的时间看这个。。。 3.3.1 指针成员与拷贝构造 关于拷贝构造函数...
    zinclee123阅读 724评论 0 0