- cpp11[meta cpp]
このページはC++11に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
C++11で導入されたnoexcept
キーワードには、以下の2つの意味がある:
ひとつは、throw
キーワードによる例外仕様の代替。関数がどの例外を送出する可能性があるかを列挙するのではなく、例外を送出する可能性があるかないかのみを指定する。例外を送出する可能性がある関数にはnoexcept(false)
を指定し、例外を送出する可能性がない関数にはnoexcept(true)
もしくはnoexcept
を指定する:
class Integer {
int value_ = 0;
public:
// getValue()メンバ関数は、例外を送出しない
int getValue() const noexcept
{
return value_;
}
};
noexcept
キーワードのもうひとつの意味は、式が例外を送出する可能性があるかどうかを判定する演算子である。noexcept(f(arg))
のようにnoexcept
演算子に式を指定することで、その式が例外を送出する可能性があるかどうかを、コンパイル時定数のbool
値として取得できる。つまり、関数に対して指定されたnoexcept
の情報を取得する:
Integer x;
static_assert(noexcept(x.getValue()), "getValue() function never throw exception");
- static_assert[link static_assert.md]
noexcept
は、代表的には以下の2つの用途で使用できる:
- パフォーマンス向上
- 例外を送出しないという保証があることで、コンパイラは例外送出によるスタック巻き戻しのためのスタックを確保する必要がなくなる
- 例外を決して送出しない強い例外安全性の保証(No-throw guarantee)
- 例外安全性で有名な問題として
stack
のpop
操作がある。要素型T
のコピーコンストラクタが例外を送出する可能性があるためにpop
の関数はT
を返すのではなく戻り値型void
とする必要があった。しかしreturn
文に指定する式が決して例外を送出しないという保証があることで、pop
の関数はT
型のオブジェクトを返せるようになる。 - 参照: ジェネリックコンポーネントにおける例外安全性 - boostjp
- 例外安全性で有名な問題として
- 例外仕様としての
noexcept
には、整数定数式を引数として指定できる。整数定数式は、bool
に変換可能であること。 noexcept
例外仕様に対してfalse
に評価される整数定数式を指定した関数は、あらゆる例外を送出する可能性がある。noexcept
例外仕様に対してtrue
に評価される整数定数式を指定した関数、もしくは引数なしでnoexcept
を指定した関数は、いかなる例外も送出してはならない。noexcept
例外仕様を指定しない関数は、一部の例外を除いて、noexcept(false)
を意味する。- デストラクタと
delete
演算子は、明示的にnoexcept(falseに評価される整数定数式)
を指定しない限り、デフォルトでnoexcept
である。
- デストラクタと
struct X {
~X(); // デストラクタはデフォルトでnoexcept(true)
// 例外を送出する可能性がある
// ※ std::vectorのコピーコンストラクタは例外を送出する
std::vector<T> getVector() const;
//std::vector<T> getVector() const noexcept(false);
// 例外を送出しない
int getValue() const noexcept;
//int getValue() const noexcept(true);
// noexceptは参照修飾と後置戻り値型の間
auto getString() const & noexcept -> std::string;
};
- noexcept[color ff0000]
noexcept
もしくはnoexcept(trueに評価される整数定数式)
が指定された関数が例外を送出した場合、std::terminate()
関数を呼び出してプログラムを異常終了させる。その際、std::terminate()
関数が呼び出される前に、スタックの巻き戻しは起こらない可能性がある。- 従来の
throw
キーワードによる例外仕様(C++03ではexception specification、C++11ではdynamic exception specificationと呼ばれる仕様)は、C++11以降で非推奨である。 noexcept
の指定可能な位置は、参照修飾の後、戻り値の型を後置する関数宣言構文の前である。
- 演算子としての
noexcept
は、引数として指定した定数式が例外を送出する可能性があるかどうかをコンパイル時に判定し、bool
型の定数値を返す
struct X {
int f() const noexcept; // noexcept例外仕様
// 外側はnoexcept例外仕様、内側はnoexcept演算子。
// メンバ関数関数f()が例外を送出しない場合、関数g()もまた例外を送出しない
int g() const noexcept(noexcept(f()))
{ return f(); }
};
X x;
// X::f()メンバ関数が例外を送出しない場合、
// isNoexprFはtrue、そうでなければfalseとなる
constexpr bool isNoexprF = noexcept(x.f());
- この演算子は
sizeof
やdecltype
と同じく、引数として指定された式は、実行時には評価されない- 上記コードの場合、
x.f()
は実行時には呼び出されない
- 上記コードの場合、
noexcept
演算子は、以下の状況でfalse
を返す:noexcept(false)
が指定されているもしくはnoexcept
が指定されていない関数、メンバ関数、関数ポインタ、メンバ関数ポインタの呼び出し。(例として、new
式からの確保関数の呼び出しといった、暗黙の呼び出し)throw
式- 実行時型チェックが行われる式として、参照型を引数とする
dynamic_cast
式の呼び出し、および多態的に振る舞う型の左辺値に対するtypeid
式の呼び出し
#include <iostream>
#include <stack>
#include <deque>
#include <type_traits>
template <class T, class Container = std::deque<T>>
class movable_stack : public std::stack<T, Container> {
using base = std::stack<T, Container>;
static_assert(std::is_nothrow_default_constructible<T>{},
"T must be nothrow default constructible");
static_assert(std::is_nothrow_move_constructible<T>{},
"T must be nothrow move constructible");
public:
// クラスのテンプレートパラメータTに対して、
// ムーブコンストラクタが例外を送出しないことを要求しているので、
// pop操作は例外を送出することなくreturnで要素を返せる。
std::pair<T, bool> move_pop() noexcept
{
if (base::empty()) {
return std::make_pair(T(), false);
}
T x = std::move(base::top());
base::pop();
return std::make_pair(std::move(x), true);
}
};
int main()
{
movable_stack<int> s;
s.push(1);
s.push(2);
s.push(3);
while (!s.empty()) {
int next_value = s.move_pop().first;
std::cout << next_value << std::endl;
}
}
- std::stack[link /reference/stack.md]
- std::is_nothrow_default_constructible[link /reference/type_traits/is_nothrow_default_constructible.md]
- std::is_nothrow_move_constructible[link /reference/type_traits/is_nothrow_move_constructible.md]
- static_assert[link static_assert.md]
- std::move[link /reference/utility/move.md]
- base::empty()[link /reference/stack/stack/empty.md]
- base::top()[link /reference/stack/stack/top.md]
- base::pop()[link /reference/stack/stack/pop.md]
- s.push[link /reference/stack/stack/push.md]
- s.empty()[link /reference/stack/stack/empty.md]
3
2
1
noexcept
機能は、ムーブコンストラクタから例外を送出することを許可する際に提案された。
ムーブ操作は基本的には例外を送出しない。そのため、例外を送出しないという、例外安全性の強い保証がしやすい仕組みと言える。ムーブに例外を送出しない保証があれば、より最適化された実装を選択できるだろう。しかし、ムーブ操作が例外を送出する可能性があるのであれば、例外を送出しないムーブ操作のための最適化された実装とそれ以外を呼び分ける仕組みが必要となる。
そういった例外を送出しない判定や指定は、従来のthrow
キーワードによる例外仕様の範囲を超えていた。そのために、noexcept
という機能が新設され、その機能で必要十分となったために従来の例外仕様は非推奨となった。
- 標準ライブラリにおける、関数に
noexcept
を付けない条件 move_if_noexcept
is_nothrow_constructible
is_nothrow_default_constructible
is_nothrow_copy_constructible
is_nothrow_move_constructible
is_nothrow_assignable
is_nothrow_copy_assignable
is_nothrow_move_assignable
is_nothrow_destructible
- C++17 非推奨だった古い例外仕様を削除
- C++17 例外仕様を型システムの一部にする
- C++20
throw()
による例外送出しない指定を削除