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:
- Explicitly calling
std::moveforreport1to use the move constructor triples the instruction count. - As noted by James McNellis in his answer below,
report2andreport3does indeed generate identical assembly with 3 times fewer instructions than explicitly callingstd::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 thestd::wstringmove 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
reportas an object or as a const-reference; in both cases you end up with an object (either the namedreportobject or an unnamed object to which thereportreference can be bound).