struct TestConstRef {
std::string str;
Test(const std::string& mStr) : str{mStr} { }
};
struct TestMove {
std::string str;
Test(std::string mStr) : str{std::move(mStr)} { }
};
观看 GoingNative 2013 后,我了解到 sink 参数应始终按值传递并随
std::move
移动。 TestMove::ctor
这个习语的正确用法是吗?有没有 TestConstRef::ctor
更好/更有效率的情况?
那么琐碎的 setter 呢?我应该使用以下习语还是传递
const std::string&
?
struct TestSetter {
std::string str;
void setStr(std::string mStr) { str = std::move(str); }
};
简单的答案是:是的。
原因也很简单,如果您按值存储,您可能需要移动(从临时值)或制作副本(从左值)。让我们用两种方式来看看这两种情况下会发生什么。
来自临时
一个限制:没有有效移动构造函数的类(例如
std::array<T, N>
),因为这样你就做了两个副本而不是一个。
来自左值(或 const 临时值,但谁会这样做......)
一个限制:相同的...类,移动类似于复制。
所以,简单的答案是,在大多数情况下,通过使用接收器可以避免不必要的副本(通过移动来替换它们)。
唯一的限制是移动构造函数与复制构造函数一样昂贵(或接近一样昂贵)的类;在这种情况下,有两个动作而不是一个副本是“最糟糕的”。值得庆幸的是,这样的类很少见(数组就是一种情况)。
有点晚了,因为这个问题已经有了公认的答案,但无论如何......这是一个替代方案:
struct Test {
std::string str;
Test(std::string&& mStr) : str{std::move(mStr)} { } // 1
Test(const std::string& mStr) : str{mStr} { } // 2
};
为什么这样会更好?考虑两种情况:
来自临时(案例
// 1
)
仅调用一个移动构造函数
str
。
来自左值(案例
// 2
)
仅调用一个复制构造函数
str
。
可能没有比这更好的了。
但是等等,还有更多:
在 caller 端不会生成额外的代码!复制或移动构造函数(可能是内联的,也可能不是)的调用现在可以存在于被调用函数的实现中(此处:
Test::Test
),因此只需要该代码的单个副本。如果使用按值参数传递,则调用者负责生成传递给函数的对象。这可能会在大型项目中增加,如果可能的话我会尽量避免。