You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

以移动赋值运算符实现移动构造:是否为反模式?有哪些禁忌场景?

Is using move assignment to implement move constructor an anti-pattern?

Great question—this is a smart way to cut down on code duplication, and it’s absolutely valid in many scenarios. It’s actually a variation of the well-known copy-and-swap idiom, adapted for move semantics. Let’s break this down.

First: It’s NOT an anti-pattern (usually)

The core idea is to centralize all your move logic in the move assignment operator, then have the move constructor delegate to it by first default-constructing the object, then moving into it. Here’s a typical implementation:

class Widget {
public:
    // Move constructor: default-construct first, then assign from the rvalue
    Widget(Widget&& other) noexcept : Widget() {
        *this = std::move(other);
    }

    // Move assignment operator: handles the actual resource transfer
    Widget& operator=(Widget&& other) noexcept {
        std::swap(this->resource_, other.resource_);
        return *this;
    }

private:
    SomeResource resource_;
};

This works because:

  • The default constructor puts the object in a valid, empty state.
  • The move assignment operator swaps (or transfers) resources from the rvalue into *this, leaving the rvalue in a valid state (as required by the C++ standard).
  • You only write your move logic once, making maintenance easier.

When you SHOULD avoid this approach

While this trick is handy, there are cases where it’s not ideal or even unsafe:

1. Default construction has non-trivial overhead

If your default constructor does expensive work (like allocating a large buffer, initializing complex objects, or acquiring locks), you’re wasting cycles by doing that work just to immediately overwrite it with the move assignment. For example:

  • If your class defaults to creating a 1MB buffer, the move constructor will allocate that buffer, then immediately swap it with the rvalue’s buffer—resulting in an unnecessary allocation + deallocation.
  • For lightweight objects (like those holding just a pointer or an integer), this overhead is negligible, but it adds up for heavyweight types.

2. Your class has const or reference members

Move assignment can’t modify const members, and you can’t rebind references once they’re initialized. If your class has either of these, delegating to move assignment won’t work—you’ll have to implement the move constructor manually to handle these members correctly.

3. noexcept requirements aren’t met

Many standard containers (like std::vector) prefer to use noexcept move constructors during reallocation—if your move constructor isn’t noexcept, they’ll fall back to using copy constructors instead, which can kill performance.

If your default constructor isn’t noexcept, then your move constructor (which calls it) can’t be noexcept either. So if you need your move constructor to be noexcept, make sure your default constructor is also noexcept—or implement the move constructor directly.

4. Move assignment assumes a non-empty state

Rare, but if your move assignment operator isn’t designed to handle an empty/default-constructed object (though it should be, per C++ standards), this can lead to undefined behavior. For example, if your move assignment tries to release a resource that wasn’t initialized (but wait—your default constructor should always leave the object in a valid state, right? So this is more of a code smell than an inherent problem with the trick itself.)

Final takeaway

This is a perfectly acceptable technique for reducing code duplication, especially for classes where default construction is cheap and noexcept. Just be mindful of the edge cases above, and you’ll be fine.

内容的提问来源于stack exchange,提问作者amc176

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:46:15