最終更新日時:
が更新

履歴 編集

例外仕様を型システムの一部にする(C++17)

概要

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が付いていること」というものが必要となった。

関連項目

参照