【问题标题】:Are there any pitfalls to using std::move() in value-oriented property setter?在面向值的属性设置器中使用 std::move() 是否有任何陷阱?
【发布时间】:2021-12-19 19:24:20
【问题描述】:

我遇到了this 对如何编写 C++ getter/setter 的回答,作者暗示当涉及到面向值的属性时,标准库中的 setter 像这样使用std::move()...

class Foo
{
    X x_;
public:
    X x() const { return x_; }
    void x(X x) { x_ = std::move(x); }
}

(直接取自上述答案的代码)

...利用移动运算符(如果指定),可能会提高性能。

这本身对我来说很有意义 - 值在通过值传递给方法时被复制一次,因此如果可以移动它们,则无需再次将它们复制到属性。但是,我对 C++ 的了解还不足以确定在所有情况下这样做都是安全的。

按值传递参数是否总是进行深层复制?还是取决于对象?考虑到 std::move() 据称“向编译器发出信号,我不在乎移动的对象会发生什么”,如果我打算保留原始对象,这可能会产生意想不到的副作用。

如果这是一个众所周知的问题,我深表歉意,我正在学习 C++,我真的很想深入了解这门语言。

【问题讨论】:

  • 是的,这是编写 setter 的好方法,但我认为我在标准库中没有见过它。 “如果我打算保留原始对象,会出现意外的副作用” 不,你不这样做,因为原始对象是 X x,它只存在于 setter 内部,无法在外部访问。
  • 我建议你阅读 Scott Meyer 写的 Modern Effective C++,深入了解std::move 和陷阱
  • 按值传递,参数通过复制或移动获取参数(取决于调用者和对象的能力)。如果对象具有值语义,那么它会在需要时进行深层复制。对象可以具有您为其编程的任何移动后置条件;大多数 C++ 标准库对象将处于有效但未指定状态,其中一些像 std::vector 或 std::unique_ptr 具有更强的后置条件保证。

标签: c++ getter c++-standard-library


【解决方案1】:

是的。

如果您始终向该参数发送右值,则按值接收参数并移动是可以的。发送左值也可以,但会比通过 const ref 接收要慢,尤其是在循环中。

为什么?似乎不是复制,而是简单地复制然后移动,其中移动在性能方面微不足道。

错误

您假设复制赋值与复制构造函数一样慢,这是错误的。

考虑一下:

std::string str_target;
std::string long1 = "long_string_no_sso hello1234";
std::string long2 = "long_string_no_sso goobye123";

str_target = long1; // allocation to copy the buffer
str_target = long2; // no allocation, reuse space, simply copy bytes

这就是为什么对于setter函数,默认情况下,你应该默认通过const ref接收,并添加一个rvalue ref来优化rvalues:

class Foo
{
    X x_;
public:
    X x() const { return x_; }

    // default setter, okay in most cases
    void x(X const& x) { x_ = x; }

    // optionally, define an overload that optimise for rvalues
    void x(X&& x) noexcept { x_ = std::move(x); }
};

唯一不适用的地方是构造函数参数和其他接收器函数,因为它们总是构造并且没有可重用的缓冲区:

struct my_type {
    // No buffer to reuse, this->_str is a totally new object
    explicit my_type(std::string str) noexcept : _str{std::move(str)}

private:
    std::string _str;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-04
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    • 2011-01-13
    • 2021-03-04
    • 1970-01-01
    • 2023-04-09
    相关资源
    最近更新 更多