Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it fine to use variables after they being used as arguments of emplace_back?

Probably, a lame question, but I keep failing to find comprehensive answer.

Parameters of std::vector::emplace_back are r-value references. As far as I understand, it is unsafe to use object after it was passed somewhere by r-value reference. I mean following:

std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;                      // unsafe, str was moved to str2

So, what will happen in following example?

std::vector<std::string> array;
std::string str("hello world");   // what if add 'const' qualifier here?
array.emplace_back(str);          // template <class... Args>
                                  // void emplace_back (Args&&... args);
std::cout << str;                 // safe or not? str was moved or copied?

I'm really confused here. My tests shows that,str is safe to use after emplace_back, but my (broken?) logic tells me that str was moved and shouldn't be used after that.

PS. Sorry for my bad English :)

like image 532
J. Doe Avatar asked Dec 10 '22 12:12

J. Doe


2 Answers

The parameters for emplace-style functions are forwarding references, which means they become lvalue references for lvalue arguments and rvalue references for rvalue arguments.

With

array.emplace_back(str);

str is an lvalue (you have not cast it to an rvalue with std::move) so it will be copied. It will retain its value after the call.

like image 192
Brian Bi Avatar answered May 19 '23 19:05

Brian Bi


A standard library object will generally be in a "valid but unspecified state".

valid but unspecified state
a value of an object that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type [Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be called unconditionally, and x.front() can be called only if x.empty() returns false. —end example]

Most often this means either empty, or retaining the original value. Moving an int probably doesn't reset its value.

Some types are more specified, for example unique_ptr always holds a nullptr after being moved from.

So, in this case

std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;   

the code is valid, but we don't know exactly what the output will be, if any. Makes it less than useful.

The idea is that you should let the variable go out of scope after being moved from, or assign it a new value to use it further.

like image 23
Bo Persson Avatar answered May 19 '23 21:05

Bo Persson