vector::push_back and std::move

vector::push_back and std::move

本文关键字:std move and push vector back      更新时间:2023-10-16

我尝试了以下代码:

#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;?tmp是右价吗?

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

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

谢谢。

第三个问题的答案:它来自andrew.punnett.评论的重新分配

tmp的类型是test&&吗?

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

tmp是右值吗?

否。标识符的任何使用都是一个左值表达式,甚至是右值引用的名称。Rvalue参考变量的访问基本上与lvalue参考变量相同;只有CCD_ 9能够区分。(通常您会使用decltype((tmp))避免说出差异。)

如果tmp的类型是test&amp;,为什么第一个pushback没有使用move构造函数?

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

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

Clang和GCC默认情况下只为vector中的一个对象腾出空间。添加第二个对象时,矢量存储被重新分配,导致对象被复制。他们为什么不动?因为move构造函数不是noexcept,所以如果它抛出异常,就不可能撤消重新分配。

至于微软风投的两个举动,有两种可能性,你可以通过实验来区分——我手头没有副本。

  1. 默认情况下,MSVC在向量内为两个对象保留了足够的空间。第二步是来自内部局部变量
  2. MSVC忽略了移动构造函数为noexcept的要求,并调用它来执行重新定位。这将是一个错误,但在这种情况下,它掩盖了一个常见的错误

如果用deque替换vector,将看不到更多副本,因为deque不允许具有可复制性。

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

#include <iostream>
#include <vector>
#include <utility> // std::move
struct Except{
Except(){}
Except(Except const &) {
std::cout<< "COPYn";
}
Except(Except&&)  {
std::cout<< "MOVEn";
}
};
struct NoExcept{
NoExcept(){}
NoExcept(NoExcept const &) noexcept {
std::cout<< "COPYn";
}
NoExcept(NoExcept&&) noexcept {
std::cout<< "MOVEn";
}
};
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<< "---nn";
}
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
---