The following code, compiled on VS2013, never invokes std::string's move constructor (checked via setting breakpoints, the const ref copy constructor is invoked instead.
#include <iostream>
#include <string>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
struct foo
{
foo(std::string& str1, std::string& str2) : _str1(str1), _str2(str2) {}
~foo() { std::cout << "Either \"" << _str1 << "\" or \"" << _str2 << "\" was returned." << std::endl; }
std::string& _str1;
std::string& _str2;
};
std::string foobar()
{
std::string str1("Hello, World!");
std::string str2("Goodbye, cruel World.");
foo f(str1, str2);
srand(time(NULL));
return (rand() % 2) ? str1 : str2;
}
int main()
{
std::cout << "\"" << foobar() << "\" was actually returned." << std::endl;
return EXIT_SUCCESS;
}
I would expect the return statement in foobar() to invoke the move constructor since I'm returning a local (the rand() is to prevent NRVO), like stated as answers to questions such as Returning std::move of a local variable
The context of this is that I'm trying to add another example for my other question here: https://softwareengineering.stackexchange.com/questions/258238/move-semantics-in-c-move-return-of-local-variables
C++11 has a special case to allow for copy/move ellision when it's a local variable and is used as the return expression from a function:
C++11 12.8/31 "Copying and moving class objects":
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
But this case for copy elision is not met because the return statement you have is not simply "the name of a non-volatile automatic object".
Later, the standard mentions that
C++11 12.8/32 "Copying and moving class objects":
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. — end note ]
This allows the move operation to be used even when the return specifies an lvalue. However, this special case only applies under the conditions of the first sentence, which are not met in the case of of your example return statement.
You can force the issue:
return (rand() % 2) ? std::move(str1) : std::move(str2);
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