このページはC++23に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
いかなる呼び出しにおいても定数式実行できないconstexpr
関数が存在しても、プログラムが不適格にならないようにする。
ただし、このような関数の存在までは許容するというだけで、定数式実行できないconstexpr
関数を定数式文脈で呼び出すと従来通りエラーとなる。
この機能が必要になった背景・経緯
これまでは、いかなる実引数での呼び出しでも定数式実行できないconstexpr
関数が存在するだけでプログラムは不適格になっていた。
例えば以下のような場合である。
int f(int x) { return x + 1; }
constexpr int g(int x) { return f(x); } // error! fはいかなるxについても定数式実行不可能
g
はconstexpr
でない関数f
を呼び出すため、プログラムの中で実際にg
が定数式実行されない場合であっても、このコードは不適格である。
これによって、定数式実行できない言語機能を使用している関数や、constexpr
でない関数を誤って呼び出すようなミスを検出することができた。
しかし、多くの標準ライブラリがconstexpr
対応を進めていくようになり、状況が変化した。
例えば、std::optional
のreset
メンバメソッドがconstexpr
に対応するのはC++23以降である。
これはstd::optional
の内部実装がunion
のアクティブメンバを更新しているからであり、これを定数式内で実行するにはC++20を待たねばならなかった(参考:定数式内での共用体のアクティブメンバの変更を許可)。
よって、以下のコードはC++17では不適格だが、C++23では正しいコードとなる。
template<typename T>
constexpr void f(std::optional<T>& opt)
{
opt.reset();
}
多くのバージョンで使われることが想定されるライブラリにおいてこれを最大限活用できるよう記述するには、constexpr
指定するかどうかをマクロで変更しなければならない。
例えばC++23からconstexpr
になる関数群に対して以下のようなマクロを使うか、
#if __cplusplus >= 202300L // 具体的な値は未定
# define MYLIB_CXX23_CONSTEXPR constexpr
#else
# define MYLIB_CXX23_CONSTEXPR
#endif
template<typename T>
MYLIB_CXX23_CONSTEXPR void f(std::optional<T>& opt)
{
opt.reset();
}
あるいはより細かく指定するためにそれぞれの機能テストマクロを使うしかなかった。
template<typename T>
#if __cpp_lib_optional >= 202106
constexpr
#endif
void f(std::optional<T>& opt)
{
opt.reset();
}
これはいかにも冗長であり、またライブラリの導入時期だけでなくその関数一つ一つがconstexpr
指定されたタイミングを意識する必要があるため、正しく指定するのは非常に難しい。
constexpr
指定を行わないことによってもこの問題は回避できるが、自身以外のユーザーが存在するライブラリの場合はconstexpr
対応が望まれる可能性があり、その場合はこの問題に対処する必要が生じる。
多くの場合、標準ライブラリ関数がconstexpr
指定されるのは、その機能が定数式実行可能になったバージョンよりも遅れる。
例えば、std::array
のoperator[]
の非const
版がconstexpr
指定されるのは、定数式内で変数の変更が許可されるC++14ではなく、その次のC++17である(参考)。
このような状況では上記のような解決策を用いてもミスを避けることは容易ではない。
現在、登場時点では定数式内で実行できなかったためにconstexpr
されていなかった多くの標準ライブラリ関数が、のちにコア言語機能が追加されてconstexpr
指定されている。
そのため、現時点でconstexpr
ではない関数も次のバージョンでconstexpr
になるかもしれず、よって関数がconstexpr
指定されているだけでエラーにする意義は薄れてきた。
以上から、コンパイル時に実行できないconstexpr
をエラーとするのは、実際にコンパイル時に呼びだされてからでよい、ということになった。
実際に定数式内で実行できない関数が定数式内で呼び出された場合、これは従来通りエラーとするほかない。 しかし、定数式内で呼び出されていないのならば、定数式内で実行できない関数が存在していてもプログラムを不適格とはしない。
仕様
constexpr
指定された関数が満たすべき条件を緩和する。
- 関数の返り値は
literal
型でなくともよい。 - 関数の実引数はどれも
literal
型でなくともよい。 - いかなる実引数が与えられても定数式実行不可能でもよい
- いかなる
template
実引数が与えられても定数式実行不可能でもよい
constexpr
指定された、=delete
指定されていないコンストラクタが満たすべき条件を緩和する。
- 非静的メンバ変数のコンストラクタは
constexpr
でなくともよい - 委譲コンストラクタの場合、委譲先のコンストラクタが
constexpr
でなくともよい
constexpr
指定された、=delete
指定されていないデストラクタが満たすべき条件を緩和する。
- 非静的メンバ変数のデストラクタは
constexpr
でなくともよい
また、陽にdefault
指定された関数は、それがconstexpr-suitable
である限り、暗黙にconstexpr
指定される。
constexpr-suitable
とは、コルーチン関数ではなく、仮想基底クラスを持つクラスのコンストラクタまたはデストラクタでもない関数を指す。
関連項目
- C++20 - constexpr関数内でのtry-catchブロックを許可
- C++20 - constexpr関数内で未評価のインラインアセンブリを許可することによる組み込み関数のconstexpr有効化