我有一个看起来像这样的API:
void WriteDefaultFileOutput(std::wostream &str, std::wstring target)
{
//Some code that modifies target before printing it and such...
}
我想知道通过这样做启用移动语义是否合理:
void WriteDefaultFileOutput(std::wostream &str, std::wstring&& target)
{
//As above
}
void WriteDefaultFileOutput(std::wostream &str, std::wstring const& target)
{
std::wstring tmp(target);
WriteDefaultFileOutput(str, std::move(tmp));
}
或者这只是编译器应该能够解决的样板吗?
答案 0 :(得分:16)
“按值传递”可以表示复制或移动;如果参数是左值,则调用复制构造函数。如果参数是rvalue,则调用移动构造函数。您不必执行任何涉及rvalue引用的特殊操作来获取带有pass by value的移动语义。
中深入探讨了这个主题答案 1 :(得分:2)
如果您打算使用不可移动的值制作副本,则应该更喜欢按值传递。例如,const&
WriteDefaultFileOutput
版本的WriteDefaultFileOutput
显式复制参数,然后使用移动版本移动副本。这意味着如果使用可移动值(xvalue或prvalue)调用WriteDefaultFileOutput(L"SomeString");
,那么它将被移动,如果使用左值调用它,它将被复制。
这意味着此函数的两种形式之间存在 no 差异。考虑一下:
wstring
在第一种情况下,它会创建一个临时的wstring
。临时值是prvalues,因此它将被“移动”到参数中(因为wstring
有一个移动构造函数)。当然,任何有价值的编译器都会忽略移动并简单地将临时构造直接放入参数中。
在你的第二种情况下,或多或少会发生同样的事情。创建临时&&
。临时值是prvalues,因此它们可以绑定到basic_string
参数类型。因此,它将使用对临时值的r值引用来调用函数的第一个版本。唯一可能的区别在于编译器不会忽略此举。即便如此,此举并不昂贵(取决于您的std::wstring myStr{L"SomeString"};
WriteDefaultFileOutput(myStr);
实施)。
现在考虑一下:
WriteDefaultFileOutput
在第一种情况下,对myStr
的调用将导致myStr
值的副本进入函数参数。
在第二种情况下,&&
是左值。 无法绑定到const&
参数。因此,它可以调用的唯一版本是&&
版本。该函数将手动构建一个副本,然后将副本与另一个副本一起移动。
效果相同。你的第一个版本的代码较少,所以请明白这个原因。
一般来说,我会说将参数作为&&
只有两个理由:
在您希望移动的所有其他情况下,只需取一个值。如果用户想要复制,请让他们复制。我想如果你想明确禁止复制参数,你可以选择&&
。但主要问题是清晰度。
如果您使用值参数,并且用户提供了可移动值,则用户提供的值将始终移动。例如,WriteDefaultFileOutput
std::move
版本的{{1}} 当然可以。但它没有必要。如果它取了一个值,那么它就已经声明了数据。
因此,如果函数采用值参数,并且您在该值中看到{{1}},那么您知道已移动的对象现在为空。 保证已被移除。