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

为什么C++标准在移动赋值运算符中使用std::exchange?

Understanding std::exchange in Move Constructors & Assignment Operators

Hey there! Let's break down why std::exchange is such a perfect fit for move semantics in C++, since you're already experimenting with it.

First, let's recap what std::exchange does in plain terms: it takes two arguments—the value you want to replace, and the new value to set it to. It returns the original value of the first argument, all in one clean operation.

Why It Shines in Move Constructors

Your intuition is spot-on: using std::exchange in a move constructor is totally reasonable, and it hits exactly the two core goals of move semantics:

  1. Transfer ownership of a resource from the source object to the new object.
  2. Leave the source object in a safe, destructible state (so when it goes out of scope, its destructor doesn't cause issues like double-frees).

Let's look at a concrete example with a dynamic pointer member:

class MyBuffer {
private:
    int* raw_data = nullptr;
    size_t size = 0;

public:
    // Move constructor using std::exchange
    MyBuffer(MyBuffer&& other) noexcept 
        : raw_data(std::exchange(other.raw_data, nullptr)),
          size(std::exchange(other.size, 0)) {}

    // Move assignment operator
    MyBuffer& operator=(MyBuffer&& other) noexcept {
        if (this != &other) {
            // Clean up our current resources first
            delete[] raw_data;

            // Transfer resources and null out the source
            raw_data = std::exchange(other.raw_data, nullptr);
            size = std::exchange(other.size, 0);
        }
        return *this;
    }

    ~MyBuffer() { delete[] raw_data; }
};

Compare to "Manual" Move Semantics

Without std::exchange, you'd have to split the transfer and reset into separate steps, especially in the constructor's initializer list:

// Equivalent behavior, but more verbose
MyBuffer(MyBuffer&& other) noexcept 
    : raw_data(other.raw_data), size(other.size) {
    other.raw_data = nullptr;
    other.size = 0;
}

The std::exchange version is cleaner because it wraps both the value retrieval and reset into a single expression. This is especially nice in initializer lists, where you can't have separate assignment statements after the colon.

It Works for More Than Just Pointers

std::exchange is generic, so it works with any type that supports assignment—like standard library containers or smart pointers:

class MyCollection {
private:
    std::unique_ptr<std::vector<int>> items;

public:
    MyCollection(MyCollection&& other) noexcept 
        : items(std::exchange(other.items, nullptr)) {}
};

Here, std::exchange transfers ownership of the unique_ptr to the new object, and sets the source's items to nullptr—no extra code needed.

Key Takeaway

std::exchange isn't just a fancy shortcut; it makes your move semantics code more concise, readable, and less error-prone. It ensures you don't forget to reset the source object's state (a common mistake when writing move operations manually), and it fits perfectly into the declarative style of member initializer lists.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:02:06