このページはC++29に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
C++29では、requires式の複合要件のnoexcept指定に、関数宣言と同じく条件となる定数式を指定できるようになる。
{ 式 } noexcept(定数式) -> 戻り値型の制約(省略可);
C++26までの複合要件では無条件のnoexceptしか書けなかったため、例外を送出しないことを条件付きで要求するには、requires式全体を書き分ける必要があった。
// C++26まで
template <typename F, bool noexc>
concept invocable = noexc
? requires(F f) { { f() } noexcept; }
: requires(F f) { f(); };
// C++29
template <typename F, bool noexc>
concept invocable = requires(F f) {
{ f() } noexcept(noexc);
};
仕様
- 複合要件の文法が、
noexceptキーワードのみを許可する形から、関数宣言と共通のnoexcept指定を許可する形へ変更される noexcept指定の定数式は、bool型へ文脈的に変換された定数式であること。定数式を指定しないnoexceptはnoexcept(true)と等価である- テンプレート引数の置換と要件の検証は、式、
noexcept指定の定数式、戻り値型の制約の順に行われる - 定数式が
trueに評価された場合、式が例外を送出する可能性があると要件は満たされない。定数式がfalseに評価された場合やnoexcept指定がない場合は、式が例外を送出する可能性があってもよい - 定数式の
boolへの変換に失敗した場合、プログラムは不適格とはならず、要件が満たされないものとして扱われる。requires式はオーバーロード解決に使われることが多く、想定外の型が渡された場合にはほかのオーバーロードの検討を継続することが期待されるためである
例
template <class F, bool NoThrow>
concept maybe_nothrow_invocable = requires(F f) {
{ f() } noexcept(NoThrow);
};
void f1();
void f2() noexcept;
int main()
{
// NoThrowがfalseの場合、f()が例外を送出する可能性があってもよい
static_assert(maybe_nothrow_invocable<decltype(f1)&, false>);
static_assert(maybe_nothrow_invocable<decltype(f2)&, false>);
// NoThrowがtrueの場合、f()が例外を送出する可能性があると要件を満たさない
static_assert(!maybe_nothrow_invocable<decltype(f1)&, true>);
static_assert(maybe_nothrow_invocable<decltype(f2)&, true>);
}
出力
この機能が必要になった背景・経緯
requires式では、式が例外を送出しないことをnoexceptで表明できたが、これを条件付きにする方法はなく、ジェネリックプログラミングで必要になった場合はrequires式を重複して書く必要があった。また、関数宣言はC++11から条件付きのnoexcept指定を持っており、複合要件だけが無条件のnoexceptしか書けないのは一貫性を欠いていた。