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

履歴 編集

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

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

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

概要

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)は定数式であり、即時関数への昇格がない
}

関連項目

参照