Consider the case when "whole" objects with move semantics enabled are returned from functions, as with std::basic_string<>
:
std::wstring build_report() const
{
std::wstring report;
...
return report;
}
Can I then realistically be expected to make the "best" choice whether to use the returned string with move semantics, as in
const std::wstring report(std::move(build_report()));
or if I should rely on (N)RVO to take place with
const std::wstring report(build_report());
or even bind a const reference to the temporary with
const std::wstring& report(build_report());
What scheme is there to make a deterministic choice of these options, if any?
EDIT 1: Note that the usage of std::wstring
above is just an example of a move semantics enabled type. It just as well be swapped for your arbitrary_large_structure
. :-)
EDIT 2: I checked the generated assembly when running a speed-optmized release build in VS 2010 of the following:
std::wstring build_report(const std::wstring& title, const std::wstring& content)
{
std::wstring report;
report.append(title);
report.append(content);
return report;
}
const std::wstring title1(L"title1");
const std::wstring content1(L"content1");
const std::wstring title2(L"title2");
const std::wstring content2(L"content2");
const std::wstring title3(L"title3");
const std::wstring content3(L"content3");
int _tmain(int argc, _TCHAR* argv[])
{
const std::wstring report1(std::move(build_report(title1, content1)));
const std::wstring report2(build_report(title2, content2));
const std::wstring& report3(build_report(title3, content3));
...
return 0;
}
The 2 most interesting outcomes:
std::move
for report1
to use the move constructor triples the instruction count.report2
and report3
does indeed generate identical assembly with 3 times fewer instructions than explicitly calling std::move
.std::move(build_report())
is wholly unnecessary: build_report()
is already an rvalue expression (it is a call of a function that returns an object by value), so the std::wstring
move constructor will be used if it has one (it does).
Plus, when you return a local variable, it gets moved if it is of a type that has a move constructor, so no copies will be made, period.
There shouldn't be any functional difference between declaring report
as an object or as a const-reference; in both cases you end up with an object (either the named report
object or an unnamed object to which the report
reference can be bound).
I'm not sure if this is standardized (as Nicol says, all optimizations are up to the compiler), but I heard STL talk about this and (at least in MSVC), RVO happens before anything else. So if there's a chance to apply RVO, then that'll happen without any action on your part. Second, when you return a temporary, you don't have to write std::move
(I think this is actually in the standard), since the return value will implicitly be treated as an rvalue.
The upshot is: Don't second-guess the compiler, just write the most natural-looking code and it'll give you the best-possible result.
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