考虑从函数返回启用了移动语义的"完整"对象的情况,如std::basic_string<>
:
std::wstring build_report() const
{
std::wstring report;
...
return report;
}
那么,是否可以像中那样,现实地期望我做出"最佳"选择,是否使用带有移动语义的返回字符串
const std::wstring report(std::move(build_report()));
或者我是否应该依靠(N)RVO来进行
const std::wstring report(build_report());
甚至用将const引用绑定到临时
const std::wstring& report(build_report());
有什么方案可以对这些选项进行确定性选择(如果有的话)
EDIT1:请注意,上面std::wstring
的用法只是启用移动语义的类型的一个示例。它还不如换成你的arbitrary_large_structure
。:-)
第2版:我在VS 2010中运行以下版本的速度优化版本时检查了生成的程序集:
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;
}
两个最有趣的结果:
- 为
report1
显式调用std::move
以使用move构造函数使指令计数增加三倍 - 正如James McNellis在下面的回答中所指出的,
report2
和report3
确实生成了相同的程序集,其指令比显式调用std::move
少3倍
std::move(build_report())
是完全不必要的:build_report()
已经是一个右值表达式(它是对按值返回对象的函数的调用),因此如果std::wstring
有一个移动构造函数,就会使用它(它确实有)。
此外,当您返回一个局部变量时,如果它是具有move构造函数的类型,则它会被移动,因此不会进行复制,句号。
将report
声明为对象或常量引用之间不应该有任何函数差异;在这两种情况下,最终都会得到一个对象(命名的report
对象或可以绑定report
引用的未命名对象)。
我不确定这是否是标准化的(正如Nicol所说,所有优化都取决于编译器),但我听到STL谈论过这一点,并且(至少在MSVC中),RVO发生在之前。因此,如果有机会申请RVO,那么你就不会采取任何行动。其次,当您返回一个临时值时,您不必编写std::move
(我认为这个实际上在标准中是),因为返回值将被隐式地视为右值。
结果是:不要对编译器进行事后猜测,只需编写看起来最自然的代码,它就会给你最好的结果。
有什么方案可以对这些选项进行确定性选择(如果有的话)?
没有,也永远不会有。
编译器执行任何类型的优化都不需要。你唯一能确定的就是编译一些代码,看看另一端会发生什么。
你最终会得到的最多的是一个通用的启发式,这是一个社区共识,人们会说,"对于大多数编译器来说,X似乎工作得最快。"但仅此而已。随着编译器跟上C++0x的速度和实现的成熟,这将需要年的时间。