我尝试了以下代码:
#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 //复制
我的问题是:
是tmp测试类型&amp;&amp ;?是tmp是一个左值?
如果tmp的类型是test&amp;&amp;,为什么第一个push_back没有使用移动构造函数?
最后一个输出来自哪里?为什么vs2013和g ++输出的结果不同?
感谢。
回答第3个问题: 它来自andrew.punnett评论的重新分配。
答案 0 :(得分:4)
类型为
tmp
test&&
?
是和否。 tmp
是test&&
类型的右值引用变量,但作为表达式的标识符tmp
具有类型test
和值类别左值。 &
永远不是表达式类型的一部分。
tmp
是左值吗?
没有。标识符的任何使用都是左值表达式,甚至是右值引用的名称。 Rvalue引用变量的访问基本上与左值引用变量相同;只有decltype(tmp)
可以区分。 (通常你会使用decltype((tmp))
来避免分辨出来。)
如果tmp的类型是test&amp;&amp;,为什么第一个push_back不使用移动构造函数?
因为右值参考的名称仍然是左值。要获得右值表达式,请使用move(tmp)
。
最后一个输出来自哪里?为什么vs2013和g ++输出不同的结果?
Clang和GCC默认只为vector
中的一个对象腾出空间。添加第二个对象时,会重新分配矢量存储,从而导致复制对象。他们为什么不感动?因为移动构造函数不是noexcept
,所以如果它抛出异常,则无法撤消重新分配。
至于MSVC的两个动作,有两种可能性,你可以通过实验来区分 - 我没有方便的副本。
noexcept
的要求,并将其调用以执行重定位。这将是一个错误,但在这种情况下,它掩盖了一个常见的错误。如果您将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
---