最終更新日時(UTC):
が更新

履歴 編集

if consteval(C++23)

概要

C++23では、constexpr関数がコンパイル時に呼ばれたかを判定するための構文として、if constevalを導入する。これは「consteval if文」と呼ばれる。

constexpr double my_fma(double a, double b, double c) {
  if consteval {
    // コンパイル時に実行される文脈
    return a * b + c;
  }
  else {
    // 実行時に実行される文脈
    return __builtin_fma(a, b, c);
  }
}

#include <iostream>
int main() {
  constexpr double static_fma = my_fma(1.0, 2.0, 3.0);
  double dynamic_fma = my_fma(1.0, 2.0, 3.0);

  std::cout << static_fma << std::endl;
  std::cout << dynamic_fma << std::endl;
}

if consteval文内は即時関数 (consteval) の文脈であり、consteval関数を呼び出すことができる。

if constevalの逆条件として、実行時かを判定するためには、if !constevalを使用する (constevalの前に!)。

constexpr double my_fma(double a, double b, double c) {
  if !consteval {
    // 実行時に実行される文脈
    return __builtin_fma(a, b, c);
  }
  else {
    // コンパイル時に実行される文脈
    return a * b + c;
  }
}

if consteval文は複合文でなければならない。

constexpr double my_fma(double a, double b, double c) {
  if !consteval {
    return __builtin_fma(a, b, c);
  }
  else
    return a * b + c; // コンパイルエラー!複合文でなければならない
}

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

constexprとconstevalの相互作用での問題

constevalを使用したプログラムを考える。

consteval int f(int i) { return i; }

constexpr int g(int i) {
  if (std::is_constant_evaluated()) {
    return f(i) + 1; // <==
  } else {
    return 42;
  }
}

consteval int h(int i) {
  return f(i) + 1;
}

定数式評価時には、関数g()と関数h()は同じことをするが、実行時には関数h()を呼び出すことはできない。また、if (std::is_constant_evaluated())文内は定数式ではあるが即時関数の文脈ではないため、即時関数f()を呼び出すことはできず、<==コメントのところで不適格となる。この文脈では、f(42)を呼び出すことはできるが、f(i)を呼び出すことはできない。

if constexpr (std::is_constant_evaluated())の問題

もうひとつの問題は、std::is_constant_evaluated()固有の問題だが、この関数はif文と使わなければならないが、誤ってif constexprで使用してしまう場合である。

constexpr size_t strlen(char const* s) {
  if constexpr (std::is_constant_evaluated()) {
    for (const char *p = s; ; ++p) {
      if (*p == '\0') {
        return static_cast<std::size_t>(p - s);
      }
    }
  } else {
    __asm__("SSE 4.2で計算…");
  }
}

この関数は常にコンパイル時の方の文脈が呼ばれてしまう。この使用間違いはコンパイラによっては警告を出力するが、誤用を防ぐのがむずかしいという問題があった。

関連項目

参照