我应该总是继续使用`sink`构造函数或setter参数吗?

时间:2013-09-07 13:12:52

标签: c++ c++11 constructor copy move

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更好/更有效的情况?


琐碎的制定者怎么样?我应该使用以下习语还是传递const std::string&

struct TestSetter {
    std::string str;
    void setStr(std::string mStr) { str = std::move(str); }
};

2 个答案:

答案 0 :(得分:21)

简单的答案是:是的。


原因很简单,如果按值存储,您可能需要移动(从临时)或复制(从l值)。让我们用两种方式检查两种情况下会发生什么。

来自临时

  • 如果你通过const-ref获取参数,临时绑定到const-ref并且不能再次移动,因此你最终会制作一个(无用的)副本。
  • 如果你按值获取参数,则从临时(移动)初始化值,然后你自己从参数移开,因此不会复制。

一个限制:没有高效移动构造函数的类(例如std::array<T, N>)因为那时你做了两个副本而不是一个副本。

来自l值(或const临时,但谁会这样做......)

  • 如果你接受const-ref的参数,那里没有任何反应,然后你复制它(不能从它移动),因此就会制作一个副本。
  • 如果您按值获取参数,则将其复制到参数中,然后从中移出,从而制作单个副本。

一个限制:相同的...移动类似于复制的类。

所以,简单的答案是,在大多数情况下,通过使用接收器,您可以避免不必要的副本(通过移动替换它们)。

单一限制是移动构造函数与复制构造函数一样昂贵(或接近昂贵)的类;在这种情况下,有两个移动而不是一个副本是“最差的”。值得庆幸的是,这类很少见(数组是一种情况)。

答案 1 :(得分:11)

有点晚了,因为这个问题已经有了一个公认的答案,但无论如何......这是一个替代方案:

struct Test {
    std::string str;
    Test(std::string&& mStr) : str{std::move(mStr)} { } // 1
    Test(const std::string& mStr) : str{mStr} { } // 2
};

为什么会更好?考虑两种情况:

来自临时(案例// 1

str只调用一个move-constructor。

来自l值(案例// 2

只为str调用一个复制构造函数。

它可能不会比这更好。

但等等,还有更多:

来电者方面没有生成其他代码!复制或移动构造函数(可能内联或不内联)的调用现在可以存在于被调用函数的实现中(此处:Test::Test),因此只需要该代码的单个副本。如果使用by-value参数传递,则调用者负责生成传递给函数的对象。这可能会在大型项目中加起来,如果可能的话,我会尽量避免使用它。