namespace std {
template <class T, class U=T>
T exchange(T& obj, U&& new_val); // (1) C++14
template <class T, class U=T>
constexpr T exchange(T& obj, U&& new_val); // (1) C++20
template <class T, class U=T>
constexpr T exchange(T& obj, U&& new_val)
noexcept(see below); // (1) C++23
}
概要
値を書き換え、書き換え前の値を返す。
効果
第1パラメータobj
で受け取った変数への参照に、第2パラメータnew_val
の値をコピー代入または可能ならムーブ代入し、代入前のobj
の状態を返す。
以下と等価の効果を持つ:
T old_val = std::move(obj);
obj = std::forward<U>(new_val);
return old_val;
戻り値
この関数を呼び出す前の、第1パラメータobj
の状態を返す。
例外
C++23から : 例外指定の式は次と等価 : is_nothrow_move_constructible_v<T> && is_nothrow_assignable_v<T&, U>
備考
この関数は、std::atomic_exchange()
関数の経験から導入された。
例
基本的な使い方
#include <iostream>
#include <utility>
int main()
{
int state = 1;
int before = std::exchange(state, 2);
std::cout << "state : " << state << std::endl;
std::cout << "before : " << before << std::endl;
}
12
#include <iostream>
#include <utility>
int main()
{
int state = 1;
int before = std::exchange(state, 2);
std::cout << "state : " << state << std::endl;
std::cout << "before : " << before << std::endl;
}
出力
state : 2
before : 1
コンテナを出力する例
#include <iostream>
#include <utility>
#include <vector>
template <class T>
void print_1(const std::vector<T>& v)
{
// カンマ区切りでvectorを出力する。
// 区切り文字は、各要素の後ではなく、前に置くと考える。
// 最初の要素のみ区切り文字を出力しない。
bool first = true;
std::cout << '{';
for (const T& x : v) {
if (!std::exchange(first, false)) {
std::cout << ',';
}
std::cout << x;
}
std::cout << '}' << std::endl;
}
// 別な書き方
template <class T>
void print_2(const std::vector<T>& v)
{
const char* delimiter = "";
std::cout << '{';
for (const T& x : v) {
std::cout << std::exchange(delimiter, ",") << x;
}
std::cout << '}' << std::endl;
}
int main()
{
const std::vector<int> v = {1, 2, 3};
print_1(v);
print_2(v);
}
44
std::cout << std::exchange(delimiter, ",") << x;
#include <iostream>
#include <utility>
#include <vector>
template <class T>
void print_1(const std::vector<T>& v)
{
// カンマ区切りでvectorを出力する。
// 区切り文字は、各要素の後ではなく、前に置くと考える。
// 最初の要素のみ区切り文字を出力しない。
bool first = true;
std::cout << '{';
for (const T& x : v) {
if (!std::exchange(first, false)) {
std::cout << ',';
}
出力
{1,2,3}
{1,2,3}
ムーブ後オブジェクトの状態をリセットする
#include <iostream>
#include <string>
#include <utility>
struct A {};
struct X {
std::string str;
int* p = nullptr;
X() = default;
// ムーブ構築しつつ、ムーブされたオブジェクトは空にする。
// ムーブ構築しただけでは、標準範囲のオブジェクトは「有効だが未規定の状態」になる
X(X&& other)
: str(std::exchange(other.str, {})),
p(std::exchange(other.p, nullptr))
{}
// 以下と等価:
// str{std::move(other.str)};
// other.str = {};
//
// p = other.p;
// other.p = nullptr;
// ムーブ代入も同様
X& operator=(X&& other) {
str = std::exchange(other.str, {});
p = std::exchange(other.p, nullptr);
return *this;
}
};
int main()
{
int value = 3;
X a;
a.str = "Hello";
a.p = &value;
X b = std::move(a);
// ムーブされたaが空になり、bとcへとデータが移動していくことを確認
std::cout << a.str << " " << a.p << std::endl;
std::cout << b.str << " " << b.p << std::endl;
X c;
c = std::move(b);
std::cout << c.str << " " << c.p << std::endl;
}
出力例
(nil)
Hello 0x7ffc560ca4cc
Hello 0x7ffc560ca4cc
バージョン
言語
- C++14
処理系
- GCC: 4.9.0 ✅
- Clang: 3.4 ✅
- ICC: ??
- Visual C++: 2015 ✅
関連項目
参照
- N3511 exchange() utility function
- N3608 exchange() utility function, revision 2
- N3668 exchange() utility function, revision 3
- P0202R3 Add Constexpr Modifiers to Functions in
<algorithm>
and<utility>
Headers std::exchange
Patterns: Fast, Safe, Expressive, and Probably Underusedstd::exchange
によるmoveしてリセットするイディオムの御紹介- P2401R0 Add a conditional noexcept specification to std::exchange