最終更新日時: 2025年07月14日 23時04分00秒 (JST)
Koichi Murase が更新

履歴 編集

function template
<utility>

# std::exchange(C++14)

    namespace std {
      template <class T, class U=T>
      T exchange(T& obj, U&& new_val);           // (1) C++14

      template <class T, class U=T>
      constexpr T exchange(T& obj, U&& new_val); // (1) C++20

      template <class T, class U=T>
      constexpr T exchange(T& obj, U&& new_val)
        noexcept(see below);                     // (1) C++23
    }

## 概要

値を書き換え、書き換え前の値を返す。

## 効果

第1パラメータ`obj`で受け取った変数への参照に、第2パラメータ`new_val`の値をコピー代入または可能ならムーブ代入し、代入前の`obj`の状態を返す。

以下と等価の効果を持つ：

    T old_val = std::move(obj);
    obj = std::forward<U>(new_val);
    return old_val;

## 戻り値

この関数を呼び出す前の、第1パラメータ`obj`の状態を返す。

## 例外

C++23から : 例外指定の式は次と等価 : `is_nothrow_move_constructible_v<T> && is_nothrow_assignable_v<T&, U>`

## 備考

この関数は、`std::atomic_exchange()`関数の経験から導入された。

## 例

### 基本的な使い方

    #include <iostream>
    #include <utility>

    int main()
    {
      int state = 1;
      int before = std::exchange(state, 2);

      std::cout << "state : " << state << std::endl;
      std::cout << "before : " << before << std::endl;
    }

#### 出力

    state : 2
    before : 1

### コンテナを出力する例

    #include <iostream>
    #include <utility>
    #include <vector>

    template <class T>
    void print_1(const std::vector<T>& v)
    {
      // カンマ区切りでvectorを出力する。
      // 区切り文字は、各要素の後ではなく、前に置くと考える。
      // 最初の要素のみ区切り文字を出力しない。

      bool first = true;

      std::cout << '{';
      for (const T& x : v) {
        if (!std::exchange(first, false)) {
          std::cout << ',';
        }
        std::cout << x;
      }
      std::cout << '}' << std::endl;
    }

    // 別な書き方
    template <class T>
    void print_2(const std::vector<T>& v)
    {
      const char* delimiter = "";

      std::cout << '{';
      for (const T& x : v) {
        std::cout << std::exchange(delimiter, ",") << x;
      }
      std::cout << '}' << std::endl;
    }

    int main()
    {
      const std::vector<int> v = {1, 2, 3};
      print_1(v);
      print_2(v);
    }

#### 出力

    {1,2,3}
    {1,2,3}

### ムーブ後オブジェクトの状態をリセットする

    #include <iostream>
    #include <string>
    #include <utility>

    struct A {};

    struct X {
      std::string str;
      int* p = nullptr;

      X() = default;

      // ムーブ構築しつつ、ムーブされたオブジェクトは空にする。
      // ムーブ構築しただけでは、標準範囲のオブジェクトは「有効だが未規定の状態」になる
      X(X&& other)
        : str(std::exchange(other.str, {})),
          p(std::exchange(other.p, nullptr))
      {}
      // 以下と等価:
      // str{std::move(other.str)};
      // other.str = {};
      //
      // p = other.p;
      // other.p = nullptr;

      // ムーブ代入も同様
      X& operator=(X&& other) {
        str = std::exchange(other.str, {});
        p = std::exchange(other.p, nullptr);
        return *this;
      }
    };

    int main()
    {
      int value = 3;

      X a;
      a.str = "Hello";
      a.p = &value;

      X b = std::move(a);

      // ムーブされたaが空になり、bとcへとデータが移動していくことを確認
      std::cout << a.str << " " << a.p << std::endl;
      std::cout << b.str << " " << b.p << std::endl;

      X c;
      c = std::move(b);
      std::cout << c.str << " " << c.p << std::endl;
    }

#### 出力例

     (nil)
    Hello 0x7ffc560ca4cc
    Hello 0x7ffc560ca4cc

## バージョン

### 言語

- C++14

### 処理系

- GCC: 4.9.0 ✅
- Clang: 3.4 ✅
- ICC: ??
- Visual C++: 2015 ✅

## 関連項目

- 非推奨だった bool 型に対するインクリメント演算子を削除

## 参照

- N3511 exchange() utility function
- N3608 exchange() utility function, revision 2
- N3668 exchange() utility function, revision 3
- P0202R3 Add Constexpr Modifiers to Functions in `<algorithm>` and `<utility>` Headers
- `std::exchange` Patterns: Fast, Safe, Expressive, and Probably Underused
- `std::exchange`によるmoveしてリセットするイディオムの御紹介
- P2401R0 Add a conditional noexcept specification to std::exchange