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 修正)コメント指摘受けました。うっかりミスです。 ↩