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


琐碎的二传手呢?我应该使用以下成语还是通过 a const std::string&

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

2 回答 2

22

简单的答案是:是的。


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

从暂时的

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

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

从左值(或 const 临时,但谁会这样做......)

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

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

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

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

于 2013-09-07T13:30:30.100 回答
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

从左值(案例// 2

只需要一个复制构造函数str

恐怕没有比这更好的了。

但是等等,还有更多:

调用方不会生成额外的代码!复制或移动构造函数的调用(可能是内联的也可能不是内联的)现在可以存在于被调用函数的实现中(这里:)Test::Test,因此只需要该代码的一个副本。如果您使用按值参数传递,则调用者负责生成传递给函数的对象。这可能会在大型项目中增加,如果可能,我会尽量避免它。

于 2013-09-07T14:27:14.710 回答