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

履歴 編集

constexpr関数内でconsteval関数を呼び出せない問題を軽減(C++23)

概要

C++23では、consteval呼び出しを含むconstexpr関数を、条件付きでconsteval関数と見なすようになる。そうすることで、定数式の文脈でのconsteval関数の使用がよりかんたんになる。

#include <algorithm>

consteval bool is_even(int x) {
  return x % 2 == 0;
}

int main() {
  constexpr int ar[] = {1, 3, 5};

  // consteval関数の非定数呼び出しにより (xは定数ではない)、
  // ラムダ式が暗黙にconstevalとなる
  static_assert(std::ranges::none_of(
    ar,
    [](int x) {
      return is_even(x); // C++20:NG, C++23:OK
    }
  ));

  // ラムダ式を明示的にconstevalにする
  static_assert(std::ranges::none_of(
    ar,
    [](int x) consteval {
      return is_even(x); // C++20:NG, C++23:OK
    }
  ));

  // is_evenは明らかに定数式評価される文脈
  // であるため使用可能
  static_assert(std::ranges::none_of(
    ar,
    is_even // C++20:NG, C++23:OK
  ));
}

仕様

  • constexpr関数が即時関数の文脈外のconsteval関数呼び出しを含み、その呼び出しが定数式ではない場合に、そのconstexpr関数は暗黙的にconsteval関数となる
  • 上記の文脈で、即時呼び出しではないconsteval関数を呼び出す場合、その文脈は暗黙的にconsteval関数となる

consteval int id(int i) { return i; }
constexpr char id(char c) { return c; }

template <typename T>
constexpr int f(T t) {
    return t + id(t);
}

auto a = &f<char>; // OK, f<char>は即時関数ではない
auto b = &f<int>;  // エラー!f<int>は即時関数

static_assert(f(3) == 6); // OK

template <typename T>
constexpr int g(T t) {    // id(42)はすでに定数であるため
    return t + id(42);    // g<int>は即時関数ではない
}

template <typename T, typename F>
constexpr bool is_not(T t, F f) {
    return not f(t);
}

consteval bool is_even(int i) { return i % 2 == 0; }

static_assert(is_not(5, is_even)); // OK

int x = 0;

template <typename T>
constexpr T h(T t = id(x)) { // h<int>は即時関数ではない
    return t;
}

template <typename T>
constexpr T hh() {           // hh<int>は即時関数
    return h<T>();
}

int i = hh<int>(); // 不適格。hh<int>()は即時関数への昇格式である

struct A {
  int x;
  int y = id(x);
};

template <typename T>
constexpr int k(int) {  // k<int>は即時関数ではない
  return A(42).y;       // A(42)は定数式であり、即時関数への昇格がない
}

関連項目

参照