Skip to content

Latest commit

 

History

History
117 lines (83 loc) · 4.7 KB

immediate_functions.md

File metadata and controls

117 lines (83 loc) · 4.7 KB

常に定数式評価するconsteval [P1073R3]

  • cpp20[meta cpp]

このページはC++20に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

概要

C++20では、関数またはメンバ関数に対して、constevalキーワードで常に定数式評価されるよう指定できる。そのような関数を即時関数(immediate function)という。

constexprと似ているが、constexprを付けた関数はコンパイル時にも実行時にも評価できるのに対し、constevalの場合は必ずコンパイル時に評価される点が異なる。

仕様

consteval指定子の付いた関数(即時関数)は、定数式評価されない場合はエラーとなる。

// P1073R3より引用
consteval int sqr(int n) {
  return n*n;
}
constexpr int r = sqr(100);  // OK
int x = 100;
int r2 = sqr(x);  // エラー: 定数式評価されない

ただし、他の即時関数の中では、即時関数が定数式評価されなくてもエラーとはならない。これは、呼び出し元が即時関数であれば、結局はコンパイル時に評価されるからである。

// P1073R3より引用
consteval int sqrsqr(int n) {
  return sqr(sqr(n)); // ここでは定数式ではないがOK
}

constexpr int dblsqr(int n) {
  return 2*sqr(n); // エラー: 定数式評価されず、呼び出し元が即時関数ではない
}

即時関数のポインタを取得することはできない。

// P1073R3より引用
using Int2Int = int(int);
Int2Int *pf = sqr; // エラー

定数式として評価できない部分があっても、評価しようとしなければエラーとならない。

consteval void f(int n) {
  if(n < 0) {
    throw "n should not be negative"; // throwは定数式として評価できないが、ここを通らなければOK
  }
}

int main() {
  f(10); // OK
  f(-1); // エラー
}

この性質は様々なチェックをコンパイル時に行うために活用できる。たとえば、std::formatにおけるコンパイル時の書式文字列チェックで使用されている。

// P1073R3より引用
consteval int sqr(int n) {
  return n*n;
}
constexpr int r = sqr(100);  // OK

int main(){
  static_assert(r == 10000, "100 * 100 == 10000");
  // int x = 100;
  // int r2 = sqr(x);  // エラー: 定数式評価されない
}

出力

この機能が必要になった背景・経緯

C++11で導入されたconstexpr指定子によって、関数を定数式評価できることを表明できるようになった。しかし、constexpr関数は定数式評価しないで実行時に評価することもできる。

それに対して、関数形式のマクロを置き換えたいであるとか、単に定数式以外では用途がない関数であるなどの理由で、必ず定数式評価される関数を作りたいという要求があり、即時関数が導入されることになった。

即時関数は実行時に評価できず、アドレスを取ることもできないので、処理系は即時関数の実体を完全に消すことができる。

また、即時関数はコンパイル時にしか取得できない情報へアクセスするコンパイラマジックな関数を表現するのにも使用できる。C++20では、ソースコード上の位置を返すstd::source_location::current()関数が即時関数になっている。

検討されたほかの選択肢

当初は constexpr!というキーワードが提案されていたが、最終的にconstevalになった。

参照