# std::exchange

< cpp‎ | utility

定义于头文件 `<utility>` |  |
template< class T, class U = T > T exchange( T& obj, U&& new_value ); |  | (C++14 起) (C++20 前)
template< class T, class U = T > constexpr T exchange( T& obj, U&& new_value ); |  | (C++20 起) (C++23 前)
template< class T, class U = T > constexpr T exchange( T& obj, U&& new_value ) noexcept(/* see below */); |  | (C++23 起)

以 `new_value` 替换 `obj` 的值，并返回 `obj` 的旧值。

### 参数

obj | - | 要替换值的对象
new_value | - | 要赋给 `obj` 的值
类型要求
- `T` 必须满足可移动构造 (MoveConstructible) 的要求。而且必须能移动赋值 `U` 类型对象给 `T` 类型对象

### 返回值

`obj` 的旧值

### 异常

（无） | (C++23 前)
`noexcept` 说明： noexcept( std::is_nothrow_move_constructible_v<T> && std::is_nothrow_assignable_v<T&, U> ) | (C++23 起)

### 可能的实现

    template<class T, class U = T>
    constexpr // C++20 起
    T exchange(T& obj, U&& new_value) noexcept(
        std::is_nothrow_move_constructible<T>::value && std::is_nothrow_assignable<T&, U>::value
    )
    {
        T old_value = std::move(obj);
        obj = std::forward<U>(new_value);
        return old_value;
    }

### 注解

能在实现移动赋值运算符和移动构造函数时使用此函数：

    struct S
    {
      int n;

      S(S&& other) noexcept : n{std::exchange(other.n, 0)}
      {}

      S& operator=(S&& other) noexcept
      {
        if(this != &other)
            n = std::exchange(other.n, 0); // 移动 n ，并于 other.n 留下零
        return *this;
      }
    };

### 示例

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

    class stream
    {
      public:

       using flags_type = int;

      public:

        flags_type flags() const
        { return flags_; }

        /// 以 newf 替换 flags_ 并返回旧值。
        flags_type flags(flags_type newf)
        { return std::exchange(flags_, newf); }

      private:

        flags_type flags_ = 0;
    };

    void f() { std::cout << "f()"; }

    int main()
    {
       stream s;

       std::cout << s.flags() << '\n';
       std::cout << s.flags(12) << '\n';
       std::cout << s.flags() << "\n\n";

       std::vector<int> v;

       // 因为第二模板形参有默认值，故能以花括号初始化列器表为第二参数。
       // 下方表达式等价于 std::exchange(v, std::vector<int>{1,2,3,4});

       std::exchange(v, {1,2,3,4});

       std::copy(begin(v),end(v), std::ostream_iterator<int>(std::cout,", "));

       std::cout << "\n\n";

       void (*fun)();

       // 模板形参的默认值亦使得能以通常函数为第二参数。
       // 下方表达式等价于 std::exchange(fun, static_cast<void(*)()>(f))
       std::exchange(fun,f);
       fun();
    }

输出：

    0
    0
    12

    1, 2, 3, 4,

    f()

### 参阅

swap | 交换两个对象的值 (函数模板)
atomic_exchange atomic_exchange_explicit (C++11)(C++11) | 原子地以非原子实参的值替换原子对象的值，并返回该原子对象的旧值 (函数模板)