Consider the following function:
std::vector<int> pushIt(std::vector<int> v){
v.push_back(10);
return v;
}
int main(){
std::vector<int> vec;
vec = pushIt(std::move(vec));
}
my assumption is that the vector is moved into the function, modified and moved back to its original spot. This should result in simmilar behavior as passing it in as a non const reference. This seems to be quite valid behavior but a colleague fears undefined behavior. Is there anything I am missing here?
I would like to do this because the current function
void currentPushIt(std::vector<int>& v){
v.push_back(10);
}
has led to a lot of problems in code review because people overlook the fact that an innocent call to currentPushIt(v)
can invalidate iterators. Making them write v=pushIt(std::move(v))
should wake them up enough that they don't make the same mistakes.
Per 1.9p15, the value computations and side effects associated with the argument are sequenced before the execution of the body of the function. So already by the time you enter pushIt
the source vec
has been moved from. The assignment is then sequenced after the execution of pushIt
, since you are actually calling the user-defined operator vector::operator=
:
vec.operator=( // sequenced after
pushIt( // the evaluation of this, which is sequenced after
std::move( // the evaluation of this
vec)))
So your code is fine.
Your colleagues fear is without reason, the snippet shown does not suffer from undefined behavior.
The snippet written, since std::vector
is a class, is equivalent of the below, and since a function parameter must have a value before the function is called it's equivalent of first calling std::move
, then pushIt
, and later vec.operator=
.
When viewed in this way it's quite clear that it's actually safe to write such code as you have done.
vec.operator= (pushIt (std::move (vec));
a = do_something (a);
I've met developers who get worried about such snippets because =
isn't a sequence-point; what if do_something
modifies a
, what happens to the left-hand side?
long story short; It doesn't matter. Even though we don't know in which order the left-hand side and the right-hand side will be evaluated it is well-defined in terms of correctness.
The left-hand side of =
is an lvalue, which can be seen as a location of where a certain value will end up. No matter the value stored at this location, the actual location will always be the same.
The right-hand side will, in this case, move from vec
. This will indoubtely change the value of vec
, but it won't change the location of where vec
is; so the result of the right-hand side will correctly be assigned to where it's supposed to.
Note: The actual assignment is sequenced, both lhs and rhs must be evaluated before this takes place (ie. before the value of rhs is assigned to the location yield by lhs), but the order of which lhs and rhs is evaluated is not set in stone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With