N3511 exchange() utility function
C++11ではアトミック操作のライブラリで、std::atomic_exchange()という関数が導入されました。この関数は、第1引数として受け取ったポインタが指す値を、第2引数の値で置き換え、戻り値として置き換え前の値を返します。
#include <iostream> #include <atomic> int main() { std::atomic<int> x(3); int before = std::atomic_exchange(&x, 2); std::cout << before << std::endl; // 変更前の値 std::cout << x.load() << std::endl; // 変更後の値 }
3 2
この経験を踏まえて、非アトミックな値に対するexchange()関数を導入しよう、というのがこの提案です。
template<typename T, typename U> T exchange(T& obj, U&& new_val) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; }
exchange()関数は、第1引数で受け取った変数への参照を、第2引数の値で置き換え、置き換え前の値を返します。
これは、いくつかの場面でプログラムを書きやすくします。以下は、コンテナをカンマ区切りで出力するプログラムです。まず、exchange()を使わない書き方は、以下のようになるでしょう。
#include <iostream> #include <vector> template <class T> void print(const std::vector<T>& v) { bool first = true; std::cout << '{'; for (const T& x : v) { if (first) { first = false; } else { std::cout << ','; } std::cout << x; } std::cout << '}'; } int main () { std::vector<int> v = {1, 2, 3}; print(v); }
{1,2,3}
カンマの数は、要素数 - 1である必要があるので、最初だけカンマ出力しないようにしています。フラグの反転とカンマの出力部分がやや冗長ですね。exchange()関数を使うと以下のように書けます。
#include <iostream> #include <vector> template<typename T, typename U> T exchange(T& obj, U&& new_val) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; } template <class T> void print(const std::vector<T>& v) { bool first = true; std::cout << '{'; for (const T& x : v) { if (!exchange(first, false)) { std::cout << ','; } std::cout << x; } std::cout << '}'; } int main () { std::vector<int> v = {1, 2, 3}; print(v); }
{1,2,3}
elseがなくなり、コードが少し短く済むようになりました。
その他、スマートポインタのリセット処理なども、exchange()関数を使うとすっきり書けるようになります。