C++14 exchange
400 Words | Read in about 2 Min | View times
Overview
C++14开始新增了std::exchange
函数,基于移动语义对资源进行转移,省去了拷贝的消耗。新增的这个函数名表面上看起来是“交换”,但实际上并不是两者进行交换,而是将后者转移给前者,后者没有变化。本节内容介绍std::exchange
和std::swap
实现上的区别。
本系列文章将包括以下领域:
本章其他内容请见 《现代C++》
std::exchange
std::exchange
虽然名为交换,但不是真正意义上的交换。更多的是实现将其中一个元素的资源转移给另一个元素,而进行转移的元素保持不变。
std::exchange声明原型
C++14开始新增std::exchange
,定义在标准库头文件<utility>
中,声明原型为:
1//C++14声明原型
2template <class T, class U = T>
3T exchange(T& obj, U&& new_value);
4
5//C++20声明原型
6template <class T, class U = T>
7constexpr T exchange(T& obj, U&& new_value);
8
9//C++23声明原型
10template <class T, class U = T>
11constexpr T exchange(T& obj, U&& new_value) noexcept(
12 std::is_nothrow_move_constructible_v<T> &&
13 std::is_nothrow_assignable_v<T&, U>
14);
这个函数使用new_value
替换obj
的值,并返回obj
的原值。
注意,经过这个函数操作后,obj
的值变化了,但是new_value
的值并没有变化。
另外类型T
必须支持移动构造,且支持从类型U
移动赋值到类型T
的对象上。
std::exchange源码解析
接下来看一下g++源码的实现:
1//<utility>
2template <typename _Tp, typename _Up = _Tp>
3constexpr inline _Tp exchange(_Tp& __obj, _Up&& __new_val)
4{
5 return std::__exchange(__obj, std::forward<_Up>(__new_val));
6}
7
8//<move.h>
9template <typename _Tp, typename _Up = _Tp>
10constexpr inline _Tp __exchange(_Tp& __obj, _Up&& __new_val)
11{
12 _Tp __old_val = std::move(__obj);
13 __obj = std::forward<_Up>(__new_val);
14 return __old_val;
15}
形参__new_val
通过完美转发传递到内部函数std::__exchange
中,内部实现就是把__obj
对象移到赋值给一个临时变量,然后自身使用__new_val
进行赋值,最后返回临时变量。返回临时变量的过程会进行NRVO返回值优化。
示例代码
std::exchange
可以用来实现移动构造函数和移动赋值操作符
1struct A
2{
3 int foo;
4
5 //利用exchange返回的是other.foo的原值,用原值初始化foo,同时other.foo的值被重置
6 A(A&& other)
7 : foo(std::exchange(other.foo, 0))
8 { }
9
10 A& operator=(A&& other)
11 {
12 //利用exchange返回的是other.foo的原值,用原值赋值给foo,同时other.foo的值被重置
13 if (this != &other)
14 foo = std::exchange(other.foo, 0);
15 return *this;
16 }
17};
- 其他例子
1#include <iostream>
2#include <vector>
3#include <utility>
4
5void f() { std::cout << "f()" << std::endl; }
6
7int main() {
8 //利用exchange来初始化vector
9 std::vector<int> v;
10 std::exchange(v, {1, 2, 3, 4});
11 for (int n : v) {
12 std::cout << n << ", ";
13 }
14 std::cout << std::endl; //1, 2, 3, 4,
15
16 //exchange也可作用于函数指针
17 void (*func)();
18 std::exchange(func, f); //等价于std::exchange(func, static_cast<void(*)()>(f));
19 func(); //f()
20
21 //exchange实现斐波那契数列
22 for (int a = 1, b = 1; a < 10; a = std::exchange(b, a + b))
23 {
24 std::cout << a << ", ";
25 }
26 std::cout << std::endl; //1, 1, 2, 3, 5, 8,
27 return 0;
28}
std::swap
std::swap
是实现两个元素互换的函数,是真正意义上的交换,交换完之后两个元素都发生了改变。
std::swap声明原型
std::swap
,定义在标准库头文件<utility>
中,声明原型为:
1//C++11-C++20前的声明原型
2template <class T>
3void swap(T& a, T& b) noexcept(
4 std::is_nothrow_move_constructible<T>::value &&
5 std::is_nothrow_move_assignable<T>::value
6);
7
8//C++20的声明原型
9template <class T>
10constexpr void swap(T& a, T& b) noexcept(
11 std::is_nothrow_move_constructible<T>::value &&
12 std::is_nothrow_move_assignable<T>::value
13);
std::swap源码解析
接下来看一下g++源码的实现:
1//<utility> -> <move.h>
2template<typename _Tp>
3constexpr inline void swap(_Tp& __a, _Tp& __b)
4{
5 _Tp __tmp = std::move(__a);
6 __a = std::move(__b);
7 __b = std::move(__tmp);
8}
总结
-
std::exchange()
使用新值替换旧值,并返回旧值,新值不变,仅是资源的转移,而不是的交换。 -
std::swap()
才是真正的交换,两个元素的值进行了互换。