Is the type of
tmp
test&&
?
Yes and no. tmp
is an rvalue reference variable of type test&&
, but the identifier tmp
taken as an expression has type test
and value category lvalue. The &
is never part of the type of an expression.
Is
tmp
a rvalue?
No. Any use of an identifier is an lvalue expression, even the name of an rvalue reference. Rvalue reference variables are accessed essentially identically to lvalue reference variables; only decltype(tmp)
can tell the difference. (Often you would use decltype((tmp))
to avoid telling the difference.)
If the type of tmp is test&&, why didn't the first push_back use move constructor?
Because the name of an rvalue reference is still an lvalue. To get an rvalue expression, use move(tmp)
.
Where did the last output come from? Why did vs2013 and g++ output different results?
Clang and GCC only made room for one object in the vector
by default. When you added the second object, the vector storage was reallocated causing the objects to be copied. Why weren't they moved? Because the move constructor is not noexcept
, so if it threw an exception, it would be impossible to undo the reallocation.
As for the two moves by MSVC, there are two possibilities, which you can distinguish by experimentation — I don't have a copy handy.
- MSVC reserved enough space inside the vector for two objects by default. The second move is from an internal local variable.
- MSVC ignored the requirement that the move constructor be
noexcept
and called it to perform relocation anyway. This would be a bug, but in this case it's covering up a common mistake.
If you replace vector
with deque
, you won't see any more copies, because deque
is not allowed to assume copyability.