简单介绍

std::exchange 是 C++ 标准库中的一个实用函数,它的主要作用是替换一个对象的值,并返回该对象的旧值。这个函数在 C++14 中引入,主要用于简化和优化代码。

具体来说,std::exchange 的函数原型如下:

template< class T, class U = T >
T exchange( T& obj, U&& new_value );

这个函数接受两个参数:一个是要替换值的对象 obj,另一个是新的值 new_value。函数将 obj 的值替换为 new_value,并返回 obj 的旧值。

std::exchange 的一个常见用途是在实现移动赋值运算符和移动构造函数时使用。例如:

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); // move n, while leaving zero in other.n
        return *this;
    }
};

在这个例子中,移动构造函数和移动赋值运算符使用 std::exchangeother.n 的值移动到 n,并在 other.n 中留下零。这样可以确保 other.n 在移动操作后处于有效状态。

总的来说,std::exchange 是一个非常实用的函数,它可以帮助你在替换对象的值的同时获取其旧值,这在许多情况下都非常有用。

为什么要使用它?

这个函数做的事情是:将obj的值设置为new_value,并返回obj原来的值。

以下是一些std::exchange的使用场景:

  1. 在交换操作中:在C++14之前,如果你想交换两个变量的值,你可能需要一个临时变量来保存其中一个变量的值。但是使用std::exchange,你可以更简洁地完成这个操作:

    int a = 5, b = 10;
    a = std::exchange(b, a);
    

    在这个例子中,std::exchangeb的值设置为a的值,并将b原来的值返回给a,从而实现了ab的交换。

  2. 在移动语义中std::exchange在处理移动语义时非常有用。例如,你可能想要在类的移动构造函数或移动赋值运算符中使用它:

    class MyClass {
    public:
        // Move constructor
        MyClass(MyClass&& other) noexcept : ptr_(std::exchange(other.ptr_, nullptr)) {}
    
        // Move assignment operator
        MyClass& operator=(MyClass&& other) noexcept {
            if (this != &other) {
                delete ptr_;  // delete resource
                ptr_ = std::exchange(other.ptr_, nullptr);  // acquire new resource
            }
            return *this;
        }
    
    private:
        int* ptr_;
    };
    

    在这个例子中,std::exchange用于获取other的资源(ptr_),并将other.ptr_设置为nullptr,以防止other在析构时删除资源。

  3. 在状态转换中std::exchange也可以用于在状态转换中保存旧状态。例如,你可能有一个状态变量,你想要改变它的值,但是你也需要知道它之前的值:

    enum class State { Idle, Running, Stopped };
    State state = State::Idle;
    
    // Start running and save the old state
    State old_state = std::exchange(state, State::Running);
    

    在这个例子中,std::exchangestate的值设置为Running,并将旧值返回给old_state

总的来说,std::exchange是一个非常实用的工具,它可以帮助你在修改一个对象的值的同时获取其旧值,从而使你的代码更加简洁和清晰。


cppreference网站介绍

定义在头文件 <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 起)


将 obj 的值替换为 new_value,并返回 obj 的旧值。

参数

  • obj:要替换其值的对象
  • new_value:要分配给 obj 的值

类型要求

  • T 必须满足 MoveConstructible 的要求。此外,必须能够将类型 U 的对象移动分配给类型 T 的对象。

返回值

obj 的旧值。

异常

  • (until C++23)
NULL

  • (since C++23)
noexcept specification:  
noexcept(
    std::is_nothrow_move_constructible_v<T> &&
    std::is_nothrow_assignable_v<T&, U>
)

可能的实现

template<class T, class U = T>
constexpr // since C++20
T exchange(T& obj, U&& new_value)
    noexcept( // since C++23
        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;
}

说明

std::exchange 可用于实现移动赋值运算符和移动构造函数:

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); // move n, while leaving zero in other.n
        return *this;
    }
};

特性测试宏

Feature-test macroValueStdComment
__cpp_lib_exchange_function201304LC++14std::exchange

示例

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

class stream
{
public:
    using flags_type = int;

public:
    flags_type flags() const { return flags_; }

    // Replaces flags_ by newf, and returns the old value.
    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;

    // Since the second template parameter has a default value, it is possible
    // to use a braced-init-list as second argument. The expression below
    // is equivalent to 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)();

    // the default value of template parameter also makes possible to

使用一个普通函数作为第二个参数。下面的表达式等同于 std::exchange(fun, static_cast<void(*)()>(f))
    std::exchange(fun, f);
    fun();

    std::cout << "\n\nFibonacci sequence: ";
    for (int a{0}, b{1}; a < 100; a = std::exchange(b, a + b))
        std::cout << a << ", ";
    std::cout << "...\n";
}

输出:

0
0
12

1, 2, 3, 4,

f()

Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …


结语

在我们的探索过程中,我们已经深入了解了Shell命令的强大功能和广泛应用。然而,学习这些技术只是开始。真正的力量来自于你如何将它们融入到你的日常工作中,以提高效率和生产力。

心理学告诉我们,学习是一个持续且积极参与的过程。所以,我鼓励你不仅要阅读和理解这些命令,还要动手实践它们。尝试创建自己的命令,逐步掌握Shell编程,使其成为你日常工作的一部分。

同时,请记住分享是学习过程中非常重要的一环。如果你发现本博客对你有帮助,请不吝点赞并留下评论。分享你自己在使用Shell命令时遇到的问题或者有趣的经验,可以帮助更多人从中学习。
此外,我也欢迎你收藏本博客,并随时回来查阅。因为复习和反复实践也是巩固知识、提高技能的关键。

最后,请记住:每个人都可以通过持续学习和实践成为Shell编程专家。我期待看到你在这个旅途中取得更大进步!


阅读我的CSDN主页,解锁更多精彩内容:泡沫的CSDN主页

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐