LoginSignup
9
6

More than 5 years have passed since last update.

なぜ std::vector<> は noexcept でない move constructor を呼ばないのか

Posted at

例外を投げない move constructor には noexcept を付ける必要があることを書いた記事はいくつか見つかります.

例えば「C++ - ムーブコンストラクタを作って push_back してみる - Qiita」では,

std::vector の push_back 時にコピーコンストラクタが呼ばれてしまう原因は、自作クラスのムーブコンストラクタに noexcept 修飾子を付けていなかったことが原因だったようだ。

のように move constructor に noexcept を付ける必要があることが示唆されています.
また std::vector<> がバッファの伸長時に std::move_if_noexcept() を使っているということも書かれています.

それではなぜ std::move_if_noexcept() を使うのでしょうか.std::move ではなぜだめなのでしょうか.

答え

ひとことで言うと例外安全性について強い保障をするためです.

std::vector<>::emplace_backpush_back のようなメンバ関数の強い例外安全性を保障するために,例外を投げる可能性のある move constructor を使うことを避けています.それを実現しているのが std::move_if_noexcept ということになります.

std::vector<>::empalce_back などで要素の追加をするとき,バッファが不足していると新たにバッファを確保し新しいバッファにもともとあった要素を copy construct または move construct します.このときに std::move_if_noexcept が使われます.

さて std::move_if_noexcept ではなく std::move を使い move constructor の中で例外が発生した場合どうなるかを考えてみます.

void foo(std::vector<A>& vec) {
  vec.empalce_back();  // 1
}

上のコードの 1 の部分で A の move constructor が例外を投げると vec は不定な状態になります.いくつかの要素を move したあとでは,それらを確実にもとの状態に戻す方法が存在しないためです.確実にもとの状態に戻すには例外が投げられないことを保障する必要があります.

一方 move constructor の代わりに copy constructor を使っていれば元のバッファに戻すだけで強い例外安全を保障することができます.

例外を投げない move constructor の場合は,そもそも例外が投げられないので新しいバッファへ確実に移動することができます.

まとめ

  • std::vector<> はバッファの伸長時に noexcept でない move constructor は使用しません.
  • noexcept がない move constructor は強い例外安全を保障するためには使えないことがあります.
  • 例外を投げない move constructor には noexcept を付けることで最適化の可能性が高まります.

参考

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6