对运算符+和/或运算符+=使用移动语义有意义吗
Does it make sense to use move semantics for operator+ and/or operator+=?
我想知道在重载运算符+和/或运算符+=时,在哪种情况下使用移动语义是有意义的。尽管在这个问题中已经解释了如何做到这一点,但我无法理解为什么要这么做。让我们考虑运算符+=。如果我只是通过引用传递右手边,并对左手边的对象进行适当的更改,那么无论如何都不会有不必要的副本。所以我们回到同一点:在这种情况下,移动语义会有益吗?
是和否
操作员+=
一般来说,Move语义对operator+=
不一定有帮助,因为您已经在修改左手边参数(this
),所以大多数情况下您已经有资源可以使用了。
不过,作为一种优化,这可能是值得的。想象一下,一个std::string
实现的默认构造函数不分配任何内存。那么std::string::operator+=(std::string&&)
可以简单地从RHS窃取资源。或者想象一下,RHS缓冲区足够大,可以容纳所有东西,但LHS不是,那么如果你可以使用RHS缓冲区时,你就是黄金:只需交换和准备即可。
因此,这可能是值得的,但你必须研究它。因此:
T& T::operator+=(T const&)
:始终存在T& T::operator+=(T&&)
:在有意义时启用移动语义
操作员+
在这里,它总是有用的(假设我们谈论的是移动语义对其有用的类)。
问题是,operator+
产生了一个临时(突然),所以它通常必须为这个临时创建资源。然而,如果它能偷走它们而不是创造它们,那肯定更便宜。
但是,您不需要提供所有过载:
T operator+(T const&, T const&)
T operator+(T&&, T const&)
T operator+(T const&, T&&)
T operator+(T&&, T&&)
(消歧必需)
不,您可以重用operator=
使用的相同技巧,并在函数签名中创建临时权限(通过复制一个参数)。如果类型是可移动的,那么将调用move构造函数,否则它将是复制构造函数,但由于您无论如何都需要临时构造函数,因此不会损失性能。
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(T const& left, T right) { right += left; return right; } // commutative
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation
增益不大(3而不是4),但好吧,我会尽我所能!
当然,对于字符串,operator+
是不可交换的(这就是为什么它是一个坏的重载),所以第二个重载的实际实现需要prepend
方法。
EDIT:遵循Move语义和运算符重载,我似乎有点过于热情了。从Ben Voigt的答案中窃取,我们得到:
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(const T& left, T&& right) { right += left; return right; }
另一方面,这似乎只适用于交换运算;-
不是这样工作的,但可能是可以适应的,/
和%
在另一方面。。。
如果要追加两个不能"移动"的字符串、向量等,那就没有意义了。但是,如果你正在附加,比如链表,其中附加列表可能是O(1)运算符,如果你愿意牺牲右手边,那么这是有意义的。
- 自定义先决条件对移动分配运算符有效吗
- 移动赋值运算符;尝试引用已删除的函数.我该如何解决这个问题?
- C++ - 没有自定义交换功能的移动分配运算符?
- 强制复制分配超过移动分配运算符
- 编译器调用复制运算符而不是移动运算符
- 对 r 值使用移动赋值运算符时的异常
- 移动语义和运算符 + 重载
- 运算符+ 的规范实现涉及额外的移动构造函数
- 在之后仍需要使用源对象时调用父移动分配运算符
- 当存在用户定义的移动分配运算符时,已删除模板移动分配运算符
- C++ - 从移动分配运算符调用复制分配
- 为什么定义移动构造函数会删除移动赋值运算符
- C ++(为什么)确实移动构造函数删除运算符=
- C++中移动赋值运算符的继承
- 为什么可以使用已删除的移动构造函数和赋值运算符移动对象?
- 删除复制构造函数是否也会删除默认的复制/移动运算符?
- std::vector::emplace() 真的在面对抛出移动构造函数/赋值运算符时提供了强大的异常保证吗?
- 为什么对象可以"moved"甚至缺少移动构造函数和移动赋值运算符?
- 链表运算符 = 移动分配时,将移动所有元素
- 是STD :: Vector,由于其分配运算符移动其成员的位置,因此是一种非常规类型