はじめに
C++の関数は、潜在的に例外を投げる可能性があります。しかし、ゲッター関数やムーブコンストラクタのように、「この関数は絶対に例外を送出しない」と分かっているものも多くあります。
C++11で導入された**noexcept指定子は、関数宣言の後ろに付けることで、その関数が例外を投げない**ことをコンパイラに伝えます。これにより、コンパイラはより積極的な最適化を行えるようになり、パフォーマンスが向上する可能性があります。
また、noexceptはプログラマにとっても、関数の仕様を明確にするための重要なドキュメントとしての役割を果たします。
【前提】C++11とは?
C++11は、2011年に正式化されたC++言語のメジャーアップデート版です。noexcept はこのC++11で導入されたため、利用するにはC++11以降に対応したコンパイラが必要です。
noexcept の使い方
1. noexcept: 無条件に例外を送出しない
noexcept(またはnoexcept(true))を付けると、その関数は例外を送出しないとマークされます。もし、noexceptが指定された関数から例外が送出されてしまうと、プログラムは即座にstd::terminateを呼び出して異常終了します。
サンプルコード
#include <iostream>
#include <string>
#include <utility> // move
using namespace std;
class Widget {
private:
string name_;
public:
// このゲッター関数は、例外を投げる処理を含まない
const string& getName() const noexcept {
return name_;
}
// ムーブコンストラクタやムーブ代入演算子は、
// noexceptにしておくと、STLコンテナなどがより効率的に扱ってくれる
Widget(Widget&& other) noexcept {
name_ = move(other.name_);
}
};
経験則: デストラクタ、ムーブコンストラクタ/ムーブ代入演算子、swap関数など、例外を投げるべきではないと一般的に考えられている関数には、積極的にnoexceptを付けるべきです。
2. noexcept(条件式): 条件付きで例外を送出しない
noexceptのカッコの中に、コンパイル時に評価される条件式を入れることで、条件に応じてnoexcept指定を有効にするか決めることができます。
これは、特にテンプレートプログラミングで、テンプレート引数の型が持つ関数のnoexcept性を引き継ぎたい場合に非常に強力です。
サンプルコード
class Resource {
public:
void process() const { /* 例外を投げる可能性あり */ }
};
class SafeResource {
public:
void process() const noexcept { /* 例外を投げない */ }
};
// テンプレート関数
template <typename T>
void execute_process(const T& item) noexcept(noexcept(item.process())) {
item.process();
}
int main() {
// is_nothrow_invocable_v はC++17の機能
cout << boolalpha;
cout << "execute_process<Resource> はnoexcept? -> "
<< noexcept(execute_process(Resource{})) << endl; // -> false
cout << "execute_process<SafeResource> はnoexcept? -> "
<< noexcept(execute_process(SafeResource{})) << endl; // -> true
return 0;
}
解説:
noexcept(item.process()):noexcept演算子は、引数に渡された式のnoexcept性をbool値で返します。itemがResource型の場合:item.process()はnoexceptではないので、falseを返します。itemがSafeResource型の場合:item.process()はnoexceptなので、trueを返します。
void execute_process(...) noexcept(...):execute_process関数自身のnoexcept性が、内部で呼び出すitem.process()のnoexcept性と連動するようになります。
まとめ
今回は、C++11のnoexcept指定子について解説しました。
noexcept: 関数が例外を送出しないことを明示する。コンパイラの最適化を助け、パフォーマンス向上に繋がる可能性がある。- デストラクタやムーブ操作には、原則として
noexceptを付けるべき。 noexcept(条件): 条件付きでnoexceptを適用し、テンプレートなどで**noexcept性を伝播**させる。
noexceptを適切に使うことで、コンパイラと、コードを読む他のプログラマの両方に対して、関数の挙動に関する重要な情報を提供し、より安全で効率的なコードを書くことができます。
