在面向价值的 属性 setter 中使用 std::move() 有什么陷阱吗?

Are there any pitfalls to using std::move() in value-oriented property setter?

我遇到了 answer to how to write C++ getters/setters and the author implies that when it comes to value-oriented properties, the setters in the standard library use the std::move()这样的...

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

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

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

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

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

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

是的。

如果您始终向该参数发送右值,则按值接收参数并移动是可以的。发送左值也可以,但会比通过 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接收,并添加一个右值ref以优化右值:

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;
};