为什么需要std :: move来调用std :: vector的move assign运算符

时间:2014-10-23 16:32:12

标签: c++ c++11 stdvector move-semantics rvalue-reference

我正在学习c ++ 11,我对移动语义和右值引用有疑问。 我的示例代码如下(C ++ Shell URL为cpp.sh/8gt):

#include <iostream>
#include <vector>

void aaa(std::vector<int>&& a)
{
    std::cout << "size of a before move: " << a.size() << std::endl;
    std::vector<int> v;
    v = a; /*std::move(a)*/
    std::cout << "size of a after move: " << a.size() << std::endl;
}

int main ()
{
  std::vector<int> foo (3,0);

  aaa(std::move(foo));

  return 0;
}

结果是:

size of a before move: 3  
size of a after move: 3

似乎在函数v = a中的aaa行未调用std :: vector的move assign运算符,否则a的大小为0而不是3。 但是,如果我将v = a更改为v = std::move(a),则输出变为

  

前移动的大小:3
   后移动的大小:0

我瘦了这次调用了std :: vector的move赋值运算符。

我的问题是为什么第一次不调用assign运算符?根据c ++参考,std :: vector有一个赋值运算符,它接受一个右值引用。

  

copy(1)vector&amp; operator =(const vector&amp; x);
  移动(2)矢量&amp; operator =( vector&amp;&amp; x );
  初始化列表(3)向量&amp; operator =(initializer_list il);

然后在第v = a行,因为a被声明为rvalue引用,所以应该调用move运算符。为什么我们仍然需要用std :: move包装一个?

非常感谢提前!

[编辑] 我认为Loopunroller和Kerrek SB都回答了我的问题。谢谢! 我不认为我可以选择两个答案,所以我会选择第一个答案。

2 个答案:

答案 0 :(得分:10)

[expr] / 6的这一说明可能会澄清正在发生的事情(强调我的):

  

[注意:表达式是xvalue,如果它是:

     
      
  • 调用函数的结果,无论是隐式还是显式,其返回类型是对象类型的右值引用,
  •   
  • 强制转换为对象类型的右值引用,
  •   
  • 一个类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是   xvalue或
  •   
  • .*指向成员的表达式,其中第一个操作数是xvalue,第二个操作数是指向数据成员的指针。
  •   
     

通常,此规则的效果是名为rvalue的引用   被视为左值和对象的未命名右值引用   被视为xvalues;对函数的右值引用被视为   左值是否命名。 - 结束记录]

根据上面的列表(项目符号1),不难看出表达式std::move(a)是x值。
a是右值引用的名称,因此是左值作为表达。

答案 1 :(得分:6)

表达式a是左值。表达式std::move(a)是一个右值。只有rvalues绑定到rvalue引用,它构成了move构造函数和移动赋值运算符。

值得重复一遍,直到它有意义:评估任何引用变量,并且还取消引用任何可解除引用的指针,产生左值。