c++ - 在面向值的属性设置器中使用 std::move() 是否有任何陷阱?
问题描述
我遇到了这个关于如何编写 C++ 获取器/设置器的答案,作者暗示当涉及到面向值的属性时,标准库中的设置器使用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;
};
推荐阅读
- haskell - Haskell 如何执行 Beta 转换以派生类型?
- firebase - how to signout user from application when his data is deleted from Authentication tab in firebase console?
- r - 如何在条形图上使用 gganimate 以使出现的每个条形在动画结束之前都不会消失?
- python - Threading using Python limiting the number of threads and passing list of different values as arguments
- javascript - 将数组转换为对象数组中的值(使用键!)
- javascript - 如何有条件地呈现来自 api 的响应
- php - WooCommerce - 启用异步回调 URL
- html - CSS:如何使图像在 flexbox 中不超过其父级高度?
- python - 使用 tensorflow 模型的预测
- c++ - 如何在 C++ 中输入 3 个错误密码后使程序自行关闭?