【C++ 实用函数 】C++ 14 std::exchange 使用说明

简介: 【C++ 实用函数 】C++ 14 std::exchange 使用说明

简单介绍

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);
  1. 在这个例子中,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_;
};
  1. 在这个例子中,std::exchange用于获取other的资源(ptr_),并将other.ptr_设置为nullptr,以防止other在析构时删除资源。
  2. 在状态转换中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);
  1. 在这个例子中,std::exchangestate的值设置为Running,并将旧值返回给old_state

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


cppreference网站介绍

定义在头文件

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 macro Value Std Comment
__cpp_lib_exchange_function 201304L C++14 std::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编程专家。我期待看到你在这个旅途中取得更大进步!

目录
相关文章
|
4月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
95 0
|
7月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
359 6
|
10月前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
448 0
|
12月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
12月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
446 6
|
12月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
109 3
|
12月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
198 0
C++ 多线程之线程管理函数
|
12月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
2070 1
|
12月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
139 1
|
12月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
183 0
C++入门6——模板(泛型编程、函数模板、类模板)