• Class / Function / Type

      std::
    • Header file

      <>
    • Other / All

    最終更新日時(UTC):
    が更新

    履歴 編集

    function template
    <utility>

    std::exchange

    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

    処理系

    関連項目

    参照