std::move_if_noexcept は、<utility> ヘッダで提供される関数テンプレートである。

安全にムーブできるならムーブし、そうでなければコピーを選びやすくするために使われる。

何をするか

std::move は、常に引数を右辺値として扱わせる。 そのため、例外を投げうるムーブしか使えない型に対して使うと、途中失敗時に元オブジェクトが変更済みになることがある。

一方、std::move_if_noexcept は次のように振る舞う。

  • ムーブコンストラクタが noexcept なら、右辺値として扱う
  • ムーブコンストラクタが noexcept でなく、コピーコンストラクタが使えるなら、左辺値のまま扱う
  • コピーできない型なら、ムーブするしかないので右辺値として扱う

つまり、「強い例外保証を保つために、ムーブとコピーを切り替えるための道具」である。

戻り値

std::move_if_noexcept(x) は、状況に応じて

  • T&&
  • const T&

のどちらかを返す。

その結果、呼び出し先ではムーブコンストラクタまたはコピーコンストラクタが選ばれる。

#include <utility>
#include <vector>
 
std::vector<int> v1 { 1, 2, 3 };
std::vector<int> v2 { std::move_if_noexcept(v1) };

このとき、std::vector<int> が安全にムーブできるならムーブされ、そうでなければコピーされる。

なぜ必要か

コピーは、失敗しても元オブジェクトを通常変更しない。 一方、ムーブは資源を移す途中で元オブジェクトの状態を変更することがある。

そのため、例外を送出しうるムーブを無条件に選ぶと、強い例外保証を保ちにくくなる。 std::move_if_noexcept は、その判断を補助する。

標準ライブラリとの関係

標準ライブラリのコンテナ(C++)は、要素の再配置時に std::move_if_noexcept と同様の考え方を使うことがある。

たとえば std::vector は、要素型のムーブコンストラクタが noexcept ならムーブを選び、そうでなければコピーを選ぶことがある。

そのため、ムーブコンストラクタnoexcept にできる型は、標準ライブラリと相性がよい。

注意

コピーできず、かつムーブが例外を投げうる型では、std::move_if_noexcept でもムーブを選ぶしかない。 その場合、操作全体として 強い例外保証 を保てないことがある。

関連

参考