このページはC++17に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
noexcept
による例外仕様が、関数の型の一部として扱われるようになる。
void f() noexcept;
void g();
void(*p1)() noexcept = f; // OK
void(*p2)() = f; // OK : noexceptから非noexceptへの変換
//void(*p3)() noexcept = g; // コンパイルエラー : 非noexceptからnoexceptに変換できない
互換性のためにnoexcept
な関数ポインタから非noexcept
な関数ポインタには変換できる。しかし、非noexcept
な関数ポインタからnoexcept
な関数ポインタには、変換できない。
これは、メンバ関数ポインタも同様である。
例外仕様は、noexcept
キーワードによるもののみで、throw
キーワードによる例外仕様は含まない。
この仕様は、ラムダ式も同様である。
void(*p1)() noexcept = []() noexcept {}; // OK
void(*p2)() = []() noexcept {}; // OK
//void(*p3)() noexcept = [](){}; // コンパイルエラー
noexcept
の違いによる関数のオーバーロードはできない。
備考
この仕様による破壊的な影響として、C++14まで有効だった以下のコードは、C++17以降ではコンパイルエラーとなる:
void g1() noexcept;
void g2();
template<class T>
int f(T*, T*);
int x = f(g1, g2); // コンパイルエラー : g1とg2の型が一致しない (関数テンプレートの推論失敗)
例
関数テンプレートで受け取った関数ポインタがnoexceptか否かを取得する
#include <iostream>
template <class R, class... Args, bool IsNoExcept>
void g(R(*f)(Args...) noexcept(IsNoExcept))
{
std::cout << std::boolalpha << IsNoExcept << std::endl;
}
void h(int, char) noexcept {}
void i(int, char) noexcept(false) {}
int main()
{
g(h);
g(i);
}
出力
true
false
この機能が必要になった背景・経緯
C++11でnoexcept
が導入されたことにより、コンパイラに最適化の機会を増やすことにつながった。この機能はそれに関連して、さらなる最適化の機会を与えることを主目的としている。
そのほか、次期標準で考えられているトランザクショナルメモリ機能において、トランザクション内で安全に使える機能の条件を決める上で、「関数にnoexcept
が付いていること」というものが必要となった。