vector :: push_back和std :: move

时间:2014-02-18 06:45:53

标签: c++ c++11 move rvalue

我尝试了以下代码:

#include <iostream>

struct test{
    test(){}
    test(test const &){
        std::cout<<__LINE__<<"\n";
    }
    test(test&&){
        std::cout<<__LINE__<<"\n";
    }
};

#include <vector>
#include <utility> // std::move
int main(){
    auto&& tmp = test();
    std::vector<test> v;
    v.push_back(tmp);
    std::cout<<__LINE__<<"\n";
    v.push_back(std::move(tmp));

    return 0;
}

vs2013编译器输出:

6 //复制

18

9 //移动

9 //移动

g ++和clang ++输出:

6 //复制

18

9 //移动

6 //复制

我的问题是:

  1. 是tmp测试类型&amp;&amp ;?是tmp是一个左值?

  2. 如果tmp的类型是test&amp;&amp;,为什么第一个push_back没有使用移动构造函数?

  3. 最后一个输出来自哪里?为什么vs2013和g ++输出的结果不同?

  4. 感谢。

    回答第3个问题: 它来自andrew.punnett评论的重新分配。

2 个答案:

答案 0 :(得分:4)

  

类型为tmp test&&

是和否。 tmptest&&类型的右值引用变量,但作为表达式的标识符tmp具有类型test和值类别左值&永远不是表达式类型的一部分。

  

tmp是左值吗?

没有。标识符的任何使用都是左值表达式,甚至是右值引用的名称。 Rvalue引用变量的访问基本上与左值引用变量相同;只有decltype(tmp)可以区分。 (通常你会使用decltype((tmp))避免分辨出来。)

  

如果tmp的类型是test&amp;&amp;,为什么第一个push_back不使用移动构造函数?

因为右值参考的名称仍然是左值。要获得右值表达式,请使用move(tmp)

  

最后一个输出来自哪里?为什么vs2013和g ++输出不同的结果?

Clang和GCC默认只为vector中的一个对象腾出空间。添加第二个对象时,会重新分配矢量存储,从而导致复制对象。他们为什么不感动?因为移动构造函数不是noexcept,所以如果它抛出异常,则无法撤消重新分配。

至于MSVC的两个动作,有两种可能性,你可以通过实验来区分 - 我没有方便的副本。

  1. 默认情况下,MSVC在矢量中为两个对象保留了足够的空间。第二步是来自内部局部变量。
  2. MSVC忽略了移动构造函数为noexcept的要求,并将其调用以执行重定位。这将是一个错误,但在这种情况下,它掩盖了一个常见的错误。
  3. 如果您将vector替换为deque,则不会再看到任何副本,因为deque不允许假定可复制性。

答案 1 :(得分:2)

Visual Studio尚不支持noexcept关键字,可能不符合push_back的异常安全性。此外,额外输出是增长时容量计算差异的结果。

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

struct Except{
    Except(){}
    Except(Except const &) {
        std::cout<< "COPY\n";
    }
    Except(Except&&)  {
        std::cout<< "MOVE\n";
    }
};

struct NoExcept{
    NoExcept(){}
    NoExcept(NoExcept const &) noexcept {
        std::cout<< "COPY\n";
    }
    NoExcept(NoExcept&&) noexcept {
        std::cout<< "MOVE\n";
    }
};

template <typename T> void Test( char const *title,int reserve = 0) {
    auto&& tmp = T();
    std::cout<< title <<"\n";
    std::vector<T> v;
    v.reserve(reserve);

    std::cout<< "LVALUE REF ";
    v.push_back(tmp);
    std::cout<< "RVALUE REF ";
    v.push_back(std::move(tmp));
    std::cout<< "---\n\n";
}
int main(){
    Test<Except>( "Except class without reserve" );
    Test<Except>( "Except class with reserve", 10 );
    Test<NoExcept>( "NoExcept class without reserve" );
    Test<NoExcept>( "NoExcept class with reserve", 10 );
}

结果在clang中:

Except class without reserve
LVALUE REF COPY
RVALUE REF MOVE
COPY
---

Except class with reserve
LVALUE REF COPY
RVALUE REF MOVE
---

NoExcept class without reserve
LVALUE REF COPY
RVALUE REF MOVE
MOVE
---

NoExcept class with reserve
LVALUE REF COPY
RVALUE REF MOVE
---