constevalについて
constexpr関数はコンパイル時でも実行時でも実行可能であったため、コンパイル時のみ実行可能な関数としてconsteval関数が定義できるようになる。
これだけならわかりやすい話だったのですがcppreference.comのコード例がキモかった。
以下引用1です。(2020/07/01時点)
consteval int f() { return 42; }
consteval auto g() { return &f; }
consteval int h(int (*p)() = g()) { return p(); }
constexpr int r = h(); // OK。
constexpr auto e = g(); // ill-formed、即時関数へのポインタは定数式の結果として許されません。
GCCではコメントの通り4行目がエラーになりませんがClangではエラーです。
h(g())とした場合はGCCでもエラーになりました。
どうしてGCCとClangで結果が違うのかわけが分からず「最適化あたりが原因かな?」とおもっていました。
ただいろいろ調べてコードを試しているなんとなく分かりました。
どうやらconsteval関数へのポインタはconsteval外部に出なければ使用可能のようです。
以下のようなコードであればClangもコンパイルが通りました。
consteval int f() { return 42; }
consteval auto g() { return &f; }
consteval int h() {int (*p)() = g(); return p(); }
constexpr int r = h(); // OK。
デフォルト引数を関数内と見るか関数外と見るかの違いだったようです。
なんではじめからこう書かんのかと思いましたが、きっと元のコード書いた人はC++11からタイムリープしてきたんだと勝手に納得しました。
まとめ
constexprの緩かったところを厳格にした機能になります。constexprのようにうっかり実行時になる心配はないのですが逆に言うと実行時とコンパイル時で関数名か名前空間を変更する必要があるので使い所はよく考える必要がありそうですね。
また上記の~~h()~~ g()2のような関数はconstexpr関数内でも使用できないため、芋づる式にconstexprが伝染していきそうなのはちょっと困りものかもしれません。
余談ですがarrayが使えるようになっているの、かなりありがたいですね。私もコンパイル時実行に関してはC++14直前ぐらいからタイムリープしてきたので実際に使ってみて感動しました。
そしてまだcmathのコンパイル時実行対応が規格に制定されていない事実に絶望しました。やっぱりGCCがNo.1
-
(7/15 修正)コメント指摘受けました。うっかりミスです。 ↩