C++14之std::exchange的使用和原理分析

目录

1.概述

2.使用

2.1.交换操作

2.2.移动语义

3.原理

4.综合示例

5.总结


1.概述

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

        它的原型定义如下:

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

注意: T 必须满足可移动构造 (MoveConstructible) 。而且必须能移动赋值 U 类型对象给 T 类型对象

2.使用

2.1.交换操作

        在std::exchage未出现之前, 我们交换两个变量的值,需要先定义一个临时的中间变量,但是使用std::exchange,你可以更简洁地完成这个操作;这对于实现一些特定的算法,尤其是需要保持变量旧值的算法时非常有用。示例如下:

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

int main() {
    std::string name = "Alice";
    std::string new_name = "Bob";
    
    std::string old_name = std::exchange(name, new_name);
    
    std::cout << "Old name: " << old_name << std::endl;
    std::cout << "New name: " << name << std::endl;
    
    return 0;
}

2.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 的实现原理相对简单。它首先返回 obj 的当前值(即“旧值”),然后将 obj 的值设置为 new_val。这通常是通过原子操作完成的,以确保在多线程环境中操作的原子性。然而,具体的实现可能因编译器和平台而异。

在 vs2019 中,std::exchange 的实现如下:

// FUNCTION TEMPLATE exchange
template <class _Ty, class _Other = _Ty>
_CONSTEXPR20 _Ty exchange(_Ty& _Val, _Other&& _New_val) noexcept(
    conjunction_v<is_nothrow_move_constructible<_Ty>, is_nothrow_assignable<_Ty&, _Other>>) /* strengthened */ {
    // assign _New_val to _Val, return previous _Val
    _Ty _Old_val = static_cast<_Ty&&>(_Val);
    _Val         = static_cast<_Other&&>(_New_val);
    return _Old_val;
}

这里有几个关键点:

1) noexcept(conjunction_v<is_nothrow_move_constructible<_Ty>, is_nothrow_assignable<_Ty&, _Other>>)确保如果赋值操作是 noexcept 的,那么整个 std::exchange 函数也是 noexcept 的。这有助于优化和异常安全性。conjunction_v是逻辑与。

2) static_cast<_Ty&&>(_Val)相当于std::move(_Val) 用于获取 _Val 的旧值,并将其转换为右值引用,以便在可能的情况下利用移动语义。

3)_Val = static_cast<_Other&&>(_New_val); 将新值赋给 _Val 相当于_Val  = std::forward<_Other>(_New_val)。std::forward 用于保持 _New_val的值类别(左值或右值)。

4.综合示例

#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
 
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();
 
    std::cout << "\n\n斐波那契数列: ";
    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()
 
斐波那契数列: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

5.总结

1) 原子性:std::exchange 提供了原子操作,这意味着在交换过程中,其他线程不会看到中间状态。这对于需要精确控制数据状态的多线程应用程序非常有用。
2) 返回值:std::exchange 返回交换前的值。这允许你在一个操作中同时获取和设置值。
3) 用途广泛:除了直接的数据交换,std::exchange 还可以与算法和其他模板一起使用,以提供更复杂的操作。
4) 与 std::swap 的区别:std::swap 也是用于交换两个值的函数,但它不保证原子性。在单线程环境中,它们可以互换使用,但在多线程环境中,std::exchange 更为安全。
5) 性能考虑:由于 std::exchange 提供了原子性保证,它可能比非原子操作(如简单的赋值)更慢。因此,在不需要原子性的情况下,使用简单的赋值或其他非原子操作可能更为高效。
6) 与 C++ 标准库的其他部分集成:std::exchange 可以与 C++ 的其他部分(如算法、容器和迭代器)无缝集成,提供灵活的编程选项。
        总之,std::exchange 是一个强大且灵活的工具,可以用于在 C++ 中进行原子值交换。然而,在使用它时,需要权衡其提供的原子性与可能的性能影响。

参考

std::exchange - cppreference.com

相关推荐

  1. C++17std::invoke: 使用原理探究(全)

    2024-04-30 10:02:01       23 阅读
  2. C++std::mem_fn使用实现原理(全)

    2024-04-30 10:02:01       15 阅读
  3. 【vuejs】$nextTick原理分析使用场景

    2024-04-30 10:02:01       11 阅读
  4. linux C函数strdup函数分析getopt_long()使用

    2024-04-30 10:02:01       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-30 10:02:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-30 10:02:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-30 10:02:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-30 10:02:01       20 阅读

热门阅读

  1. 数据结构-图

    2024-04-30 10:02:01       9 阅读
  2. openlayers6在uniapp中结合rendjs

    2024-04-30 10:02:01       10 阅读
  3. Go 语言函数

    2024-04-30 10:02:01       10 阅读
  4. Redis八股文

    2024-04-30 10:02:01       10 阅读
  5. React

    2024-04-30 10:02:01       13 阅读
  6. 各种优化器及其优缺点(SGD,RMSProp,Adam及其变种)

    2024-04-30 10:02:01       16 阅读
  7. 机器学习-- 爬虫IntelliScraper 重大更新说明

    2024-04-30 10:02:01       14 阅读
  8. uni-app + vant 实现可搜索的popup

    2024-04-30 10:02:01       11 阅读
  9. Python基本数据类型

    2024-04-30 10:02:01       14 阅读
  10. iOS 创建开源库时如何使用图片和xib资源

    2024-04-30 10:02:01       13 阅读
  11. go设计模式之组合设计模式

    2024-04-30 10:02:01       15 阅读