std::exchange
的版本引入与底层原理std::exchange
是 C++14 引入的标准库工具函数,定义在
头文件中。
template <typename T, typename U = T>
T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj); // 保存旧值
obj = std::forward<U>(new_value); // 赋予新值
return old_value; // 返回旧值
}
关键机制:
std::move
避免旧值的拷贝std::forward
保持新值的值类别(左值/右值)std::vector<int> v1{1, 2, 3};
std::vector<int> v2 = std::exchange(v1, {4, 5, 6});
// 现在:
// v1 = {4, 5, 6}
// v2 = {1, 2, 3}
在 C++98/03 中,需要手动实现类似功能:
template <typename T, typename U>
T legacy_exchange(T& obj, const U& new_value) {
T old_value = obj; // 拷贝构造(无移动语义)
obj = new_value; // 拷贝赋值
return old_value;
}
缺陷:
// 安全转移资源所有权
std::unique_ptr<Resource> acquire_resource() {
static std::unique_ptr<Resource> res = std::make_unique<Resource>();
return std::exchange(res, nullptr); // 原子性转移
}
// 需要额外临时变量
std::auto_ptr<Resource> acquire_resource() {
static std::auto_ptr<Resource> res(new Resource);
std::auto_ptr<Resource> tmp = res; // 拷贝(所有权转移)
res.reset(); // 显式置空
return tmp; // 返回临时变量
}
假设操作 std::string
对象:
操作 | 指令数(x86-64) | 备注 |
---|---|---|
C++98 手动交换 | 15 | 含冗余拷贝 |
std::exchange |
8 | 完全利用移动语义 |
编译器优化后 | 5 | 部分操作可内联消除 |
constexpr
支持(可在编译期使用)constexpr int demo() {
int x = 10;
return std::exchange(x, 20); // 返回10,x变为20
}
static_assert(demo() == 10);
维度 | C++98 | C++11(含exchange ) |
---|---|---|
资源转移效率 | 需要拷贝 | 移动语义零开销 |
代码安全性 | 容易遗漏重置操作 | 原子性操作 |
表达能力 | 需3行代码 | 单函数调用 |
适用场景 | 基本类型 | 支持移动语义的所有类型 |
最佳实践:在资源管理(如智能指针、锁等)和状态机实现中优先使用
std::exchange
。
std::exchange
和 std::swap
功能不同
std::exchange
:将对象替换为新值,并返回旧值,不要求新值的类型与原对象类型相同(只要能转换)。std::swap
):交换两个对象的值,要求类型完全一致。操作对象数量
std::exchange
:操作单个对象(替换其值)。返回值
std::exchange
:返回旧值(原对象的值)。应用场景
std::exchange
:适合需要替换值并保留旧值的场景,如初始化智能指针、重置状态等。std::exchange
的使用:
#include
#include
int main() {
int a = 10;
int old_value = std::exchange(a, 20); // a 被替换为 20,返回旧值 10
std::cout << "a: " << a << ", old_value: " << old_value << std::endl; // 输出: a: 20, old_value: 10
return 0;
}
普通交换的使用:
#include
#include
int main() {
int a = 10;
int b = 20;
std::swap(a, b); // a 和 b 的值互换
std::cout << "a: " << a << ", b: " << b << std::endl; // 输出: a: 20, b: 10
return 0;
}
std::exchange
是“替换并返回旧值”,适用于需要原子性更新并获取旧值的场景。std::exchange
的原子性std::exchange
保证对单个对象的修改是不可分割的。具体来说:
std::exchange
修改一个对象时,其他线程不会看到“部分修改”的中间状态。int a = 10;
int old = std::exchange(a, 20); // 原子性替换 a 的值并返回旧值
这里,a
的值要么是 10(替换前),要么是 20(替换后),不会出现中间状态。
当 std::exchange
用于 std::atomic
类型时,它提供硬件级别的原子操作,等价于 std::atomic::exchange
:
#include
std::atomic<int> atomic_a(10);
int old = std::exchange(atomic_a, 20); // 原子操作,等价于 atomic_a.exchange(20)
此时:
std::exchange
操作同一个原子变量时,不会出现数据竞争。std::memory_order_seq_cst
(顺序一致性),可通过 std::atomic
的重载版本指定其他内存顺序。普通赋值操作不保证原子性,可能被其他线程观察到中间状态:
int a = 10;
int temp = a; // 步骤1:读取旧值
a = 20; // 步骤2:写入新值
// 其他线程可能在步骤1和步骤2之间观察到 a 的值
而 std::exchange
将这两个步骤合并为一个原子操作。
std::exchange
对单个对象的操作是原子的,但如果多个对象的操作需要原子性(如交换两个变量),仍需额外同步。int
、std::vector
),std::exchange
仅保证操作的不可分割性,但不保证线程安全(多个线程同时访问仍需锁)。std::exchange
的原子性体现在:
std::atomic
类型提供硬件级原子操作,保证线程安全。这使得它在资源管理(如智能指针)、状态更新(如标志位翻转)等场景中特别有用,能避免竞态条件和数据不一致问题。
g++ $^ -o $@ -std=c++14 -fno-elide-constructors
// lhr::unique_ptr ptr1 = func1();
lhr::unique_ptr<int> func1()
{
return lhr::unique_ptr<int>(new int(520));
}
raw cons // 原始构造,对应 new int(520)
move cons // 从临时对象移到返回值
delete // 临时对象析构
move cons // 返回值移到 ptr1
delete // 返回值析构
delete // ptr1 析构
// lhr::unique_ptr ptr2 = func2();
lhr::unique_ptr<int> func2()
{
auto p = lhr::unique_ptr<int>(new int(520));
return p;
}
raw cons // 构造p
move cons // p移动到临时对象
delete // p析构
move cons // 临时对象移动返回值
delete //临时对象析构
move cons // 返回值移动 ptr2
delete // 返回值
delete //ptr2
g++ $^ -o $@ -std=c++14
// lhr::unique_ptr ptr1 = func1();
lhr::unique_ptr<int> func1()
{
return lhr::unique_ptr<int>(new int(520));
}
raw cons //原地构造ptr1
delete
// lhr::unique_ptr ptr2 = func2();
lhr::unique_ptr<int> func2()
{
auto p = lhr::unique_ptr<int>(new int(520));
return p;
}
raw cons
delete
g++ my.cc -o main -std=c++17 -fno-elide-constructors
// lhr::unique_ptr ptr1 = func1();
lhr::unique_ptr<int> func1()
{
return lhr::unique_ptr<int>(new int(520));
}
raw cons
delete
// lhr::unique_ptr ptr2 = func2();
lhr::unique_ptr<int> func2()
{
auto p = lhr::unique_ptr<int>(new int(520));
return p;
}
raw cons
move cons
delete
delete
g++ my.cc -o main -std=c++17
// lhr::unique_ptr ptr1 = func1();
lhr::unique_ptr<int> func1()
{
return lhr::unique_ptr<int>(new int(520));
}
raw cons
delete
// lhr::unique_ptr ptr2 = func2();
lhr::unique_ptr<int> func2()
{
auto p = lhr::unique_ptr<int>(new int(520));
return p;
}
raw cons
delete
c++11/14 使用移动构造代替拷贝优化
c++17使用RVO(返回值优化)代替移动构造优化