C++ 向量::推回和标准::移动
我尝试了以下代码:C++ 向量::推回和标准::移动,c++,c++11,move,rvalue,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 <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;
}
#包括
结构测试{
test(){}
测试(测试常数&){
标准::cout
是tmp
test&
的类型吗
是和否。tmp
是类型为test&
的右值参考变量,但作为表达式的标识符tmp
具有类型test
和值类别左值。&
决不是表达式类型的一部分
tmp
是右值吗
否。标识符的任何使用都是一个左值表达式,甚至是右值引用的名称。右值引用变量的访问方式基本上与左值引用变量相同;只有decltype(tmp)
可以区分差异。(通常您会使用decltype((tmp))
来避免区分差异。)
如果tmp的类型是test&,为什么第一个push_back没有使用move构造函数
因为右值引用的名称仍然是左值。若要获取右值表达式,请使用move(tmp)
最后的输出来自哪里?为什么vs2013和g++输出不同的结果
默认情况下,Clang和GCC只为向量中的一个对象腾出空间。添加第二个对象时,向量存储被重新分配,导致对象被复制。为什么它们没有被移动?因为移动构造函数不是noexcept
,因此如果它引发异常,则无法撤消重新分配。
至于MSVC的两个动作,有两种可能,你可以通过实验来区分——我手边没有副本
默认情况下,MSVC在向量中为两个对象保留了足够的空间。第二步是从内部局部变量移动
MSVC忽略了move构造函数必须是noexcept
的要求,并调用它来执行重新定位。这可能是一个bug,但在这种情况下,它掩盖了一个常见错误
如果将vector
替换为deque
,您将看不到更多副本,因为deque
不允许假定可复制性。Visual Studio尚不支持noexcept关键字,并且可能不符合push_back的异常安全性。此外,额外的输出是cap上的差异造成的人口增长计算
#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 );
}
基本正确,但对构造函数的额外调用只是因为向量正在重新定位,而不是使用“临时内部变量”就在向量创建之后。这里不是有一些引用崩溃的诡计吗,例如,tmp
是RHS返回的吗?它与整个“通用引用”是一样的。所以test&&与test&?@andrew.punnett没有优势。谢谢,我只是假设GCC和Clang保留了足够的空间。(test
应该只有一个字节,动态分配少于几个字的内容很少有好处。)aa而且,MSVC可能出现错误。典型。@user1535111取决于您所说的“优势”是什么意思。右值引用和左值引用之间的最大区别在于它们的初始化方式。尝试替换auto&
,看看会发生什么。我已经修改了我的答案以扩展Andrew的评论。最有效的解决方案是v.emplace_back();
,它既不需要复制也不需要移动。注意:v.emplace_back()
如果对象是在call@DarkWandererv.emplace_back();
直接在向量的内存中构造对象。在调用之前没有创建任何对象。@FredOverflow:Nope。MyClass实例;v.emplace_back(实例);
。虽然我移动的想法是错误的,但它将通过复制构造函数进行复制。
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
---