Does std::move work with lvalue references? How does std::move work on standard containers?
Solution 1
std::move
doesn't do a move. It actually casts the lvalue reference to an rvalue reference. In this case, the result of the move is a const A &&
(which is totally useless by the way).
std::vector
has an overload for a const A &
and a A &&
, so the overload with const A &
will get chosen and the const A &&
is implicitly casted to const A &
The fact that std::move
can be called on const objects, is strange/unexpected behavior for most programmers, though it somehow is allowed. (Most likely they had a use case of it, or none to prevent it)
More specific for your example, the move constructor of the class A will get called. As A is a POD, this most likely will just do a copy as all bits just have to move/copied to the new instance of A.
As the standard only specifies that the original object has to be in a valid though unspecified state, your compiler can keep the bits in A in place and doesn't have to reset them all to 0. Actually, most compilers will keep these bits in place, as changing them requires extra instructions, which is bad for performance.
Solution 2
Created a snippet to show it. Though in your example default constructor will be called, but you get the idea.
#include <vector>
#include <iostream>
struct A {
int a[100];
A() {}
A(const A& other) {
std::cout << "copy" << std::endl;
}
A(A&& other) {
std::cout << "move" << std::endl;
}
};
void foo(const A& a) {
std::vector<A> vA;
vA.push_back(std::move(a));
}
void bar(A&& a) {
std::vector<A> vA;
vA.push_back(std::move(a));
}
int main () {
A a;
foo(a); // "copy"
bar(std::move(a)); // "move"
}
iammilind
"Among programming languages, I am C++ ..." — BG 10.19...
Updated on July 18, 2022Comments
-
iammilind almost 2 years
#include <vector> struct A { int a[100]; }; void foo (const A& a) { std::vector<A> vA; vA.push_back(std::move(a)); // how does move really happen? } int main () { A a; foo(a); }
The above code compiles fine. Now everywhere it's written that
move
avoids copying.
Following are my queries:- Does the
move
really work when one deals with a lvalue [non]-const
reference? - Even with "rvalue reference", how is the copy avoided when the object is inserted into a standard container like above?
e.g.
void foo (A&& a) { // suppose we invoke this version std::vector<A> vA; vA.push_back(std::move(a)); // how copy is avoided? }
- Does the
-
Admin over 7 yearsWhy is it strange? The fact that
const T &
can bind to rvalues is not new in C++11. -
JVApen over 7 yearsLet me clearify that sentence, I meant the existance of
const A &&
-
Steve Jessop over 7 yearsIt stops being strange when you consider that in C++, adding
std::move
into a bit of code that otherwise would result in a copy, and in general the Movable concept mean "move if possible otherwise copy". They don't mean "move if possible otherwise fail to compile". -
Admin over 7 yearsOkay, then still, why is it strange?
const
-qualified rvalues are not new in C++11 either. :) -
Miles Budnek over 7 yearsNo copy is avoided. Your two
foo
functions will behave exactly the same since you've not defined a move constructor (nor does yourA
class have any movable resources). -
JVApen over 7 yearsIndeed, as A is a POD, it will always be copied, unless in some sanitizing mode.
-
Holt over 7 years@MilesBudnek Move constructor is implicitly defined in this case, and while it is true that in the end you will have to do a copy of
A::a
because an array is not movable, the first version offoo
will callpush_back(A const&)
while the second will callpush_back(A&&)
, so these functions do not behave exactly the same. -
Steve Jessop over 7 yearsYou could probably even make the difference into observable behaviour if you specialised
std::allocator<A>
to detect whatconstruct
overload the vector calls. That's the sense in which it "is" a move even though it actually does the same as copy forA
. -
laike9m about 6 yearsI think you should make it more clear in "More specific for your example, the move constructor of the class A will get called.". Like you said, the two examples behave differently, so only in the second example the move constructor gets called.
-
Slav almost 5 yearsit would be more demonstrative if you write
foo(std::move(a));
-
Spencer over 4 yearsOK, so what if there's a non-copyable object with a move constructor but no copy constructor? Will it select the move constructor? (yeah, ideally the copy constructor will be deleted).