GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • cpprefjp - C++日本語リファレンス
  • GitHub Project

This view

  • Original page
  • New address

GeezerVison is showing cpprefjp.github.io — constexpr [N2235] - cpprefjp C++日本語リファレンス

  1. 言語機能
  2. C++11
  3. constexpr [N2235]

最終更新日時: 2026年11月10日 14時22分26秒 (JST)
rotarymars が更新

履歴 編集

constexpr [N2235](C++11)

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

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

概要

constexprは、汎用的に定数式を表現するための機能である。

constexprは、「constant expression (定数式)」の略語である。

この機能を使用することで、コンパイル時に値が決定する定数、コンパイル時に実行される関数、コンパイル時にリテラルとして振る舞うクラスを定義できる。

定数式の例として、パラメータの値を2乗して返す関数square()は、以下のように記述する:

#include <cassert>

constexpr int square(int x)
{
  return x * x;
}

int main()
{
  constexpr int compile_time_result = square(3);
  static_assert(compile_time_result == 9, "result must be 9");

  int runtime_result = square(3);
  assert(runtime_result == 9);
}

関数square()の先頭にconstexprキーワードを付けることで、その関数は、コンパイル時と実行時、両方で呼び出せる関数となる。

constexpr関数がコンパイル時に呼び出されるか、実行時に呼び出されるかは、その関数の結果を受け取る、左辺によって決定される。引数が定数でかつ、左辺がconstexpr修飾された変数であれば、右辺の関数はコンパイル時に呼び出される。そうでなければ、関数は実行時に呼び出される。

constexpr変数は、書き換えることができない定数となる。

constexpr関数もまた、その内部で変数の書き換えはできず、戻り値の型をvoidにすることもできない。変数の書き換えをせずに、計算した結果を返す必要がある。また、constexpr関数には、その本体において、return文ひとつのみで処理を行わなければならない、という制限がある。そのため、条件分岐にはif文の代わりに条件演算子を、ループにはwhile文やfor文の代わりに再帰を使用する必要がある。

constexpr int min(int a, int b)
{
  // 条件分岐には条件演算子?:を使用する
  return a < b ? a : b;
}

constexpr int factorial(int n)
{
  // ループには再帰を使用する
  return n == 0 ? 1 : n * factorial(n - 1);
}

int main()
{
  constexpr int min_val = min(2, 3);
  static_assert(min_val == 2, "result must be 2");

  constexpr int factorial_val = factorial(5);
  static_assert(factorial_val == 120, "result must be 120");
}

constexprにできる変数の型は、リテラル型に分類される必要がある。これには、整数型、浮動小数点数、リテラル型の配列、リテラル型のみをメンバ変数として持つクラスなどが含まれる。

ユーザー定義のクラスで、コンストラクタを定義する場合には、constexprをコンストラクタの宣言に付ける。メンバ関数も、コンパイル時に呼び出す必要がある場合には、constexpr修飾する。この時C++14でも適格なコードであるためにはconstメンバ関数である必要がある。メンバ変数の宣言にconstexprを付ける必要はない。

class Integer {
  int value_;
public:
  constexpr Integer(int value)
    : value_(value) {}

  constexpr int get() const
  { return value_; }
};

int main()
{
  constexpr Integer x = 3;
  static_assert(x.get() == 3, "x value must be 3");
}

仕様

constexpr関数

  • constexpr関数の戻り値の型、およびパラメータの型は、リテラル型でなければならない
  • constexpr関数の戻り値の型、およびパラメータの型は、非const参照にはできない
  • constexpr関数の本体は、以下の要素だけを含むことができる:
    • 空文
    • static_assert宣言
    • 型の別名定義
    • using宣言と、usingディレクティブ
    • 唯一のreturn文
  • constexpr関数は暗黙的にインラインとなる
  • constexpr関数を再帰的に呼び出せる深さは、512回以上であることが、コンパイラに推奨される
  • constexpr関数は数学的に結果が定義された計算式、および結果が値の表現範囲内である計算式を記述できる
    • つまり、整数をゼロ割りした場合は、プログラムは不適格となる
  • main()関数は、constexpr関数として定義できない
  • throw文は書けるが、それがコンパイル時に評価された場合には、プログラムは不適格となる。

// コンパイル時にtrueを渡すと、コンパイルエラーになる
constexpr int f(bool b)
{ return b ? throw -1 : 0; }

constexpr変数として使用するユーザー定義クラス

  • コンストラクタを明示的に定義する場合には、コンストラクタをconstexpr修飾する
  • 暗黙的に定義されるコンストラクタは、自動的にconstexpr修飾される
  • constexprコンストラクタのパラメータの型は、リテラル型でなければならない。
  • constexprコンストラクタには、関数tryブロックは使用できない
  • constexprコンストラクタの本体は、空でなければならない
  • 仮想基底クラスを持ってはならない
  • 基底クラス、および非静的メンバ変数は、メンバ初期化子を使用して初期化しなければならない

constexprメンバ関数

  • constexprメンバ関数は自動的にconst修飾される (明示的にconst修飾することもできる)
  • 仮想関数は、constexpr関数として定義できない

備考

標準ライブラリ関数へのconstexpr指定

標準ライブラリの関数にconstexprが付くかどうかは、規格が明示的に要求している場合に限られる。処理系が独自の判断で、規格が要求していない標準ライブラリ関数のシグニチャにconstexprを付けることは許可されていない。

これは、ある処理系では定数式で使えるが別の処理系では使えない、といった移植性の問題を防ぐためである。

浮動小数点数演算での注意

constexpr関数での浮動小数点数は、コンパイル時に実行するとコンパイル環境で計算が行われ、実行時に実行すると実行環境で計算が行われる。これによって、コンパイル時と実行時で、結果が異なる可能性がある。

コンパイル時と実行時で、統一したエラー報告

constexpr関数では、コンパイル時にはエラー報告にstatic_assert、実行時にはthrow文やassert()関数マクロなどを使用する必要があり、標準機能の範囲内では、コンパイル時と実行時で、エラー報告を統一的に行うことが難しい。

そういった問題を解決するために、ユーザーコミュニティで、エラー報告を統一的に行う仕組みが作られている。ここでは、その問題に取り組んでいる場所へのリンクを記載する。

  • コンパイル時/実行時両用アサート - ボレロ村上 - ENiyGmaA Code

グローバル変数の初期化順

並行プログラミングについて考えよう。スレッド間の排他処理を行うミューテックスをグローバル変数に保持したとき、ミューテックスの初期化が、スレッドの開始よりも必ず先に行われてほしい。

extern std::mutex m;
std::thread t1{job1};
std::thread t2{job2};

そんな状況のために、std::mutexクラスのデフォルトコンストラクタには、constexpr修飾が行われている。そうすることで、その型の非ローカル変数を作ったときに、その変数は他の任意のスレッド開始よりも先に行われることが保証される。

  • 雑貨's tumblr - Constant Initialization
  • mutexのconstexprコンストラクタ

文字列処理

constexprでは、new演算子やmalloc()関数が使用できないために、可変長の配列や文字列を扱うことが難しい。

C++11段階の標準ライブラリでも、そのような機能は標準では提供されていない。

こういった問題を解決するために、ユーザーコミュニティでそのような機能が作られている。ここでは、その問題に取り組んでいる場所へのリンクを記載する。

  • Sprout.String - constexpr 文字列クラス - ボレロ村上 - ENiyGmaA Code

constexprで文字列が扱えることにより、printf()のフォーマット文字列や、正規表現の間違いをコンパイル時に検出することができるようになる。

コンパイル時再帰回数

コンパイラによっては、コンパイルオプションでコンパイル時におけるconstexpr関数の再帰回数の上限を設定できる。

GCCとClangでは、-fconstexpr-depth=オプションで設定できる:

g++ main.cpp -fconstexpr-depth=1024

Visual C++ 2015では、/constexpr:depthオプションで設定できる:

cl.exe /constexpr:depth1024 main.cpp

ここの1024を任意の値に変更することで、再帰回数の上限を設定できる。

GCC 5.2、Clang 3.7、Visual C++ 2015時点で、3つともデフォルトは512回。

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

constexprの主な目的は、数値型のプロパティを取得するクラスstd::numeric_limitsの、プロパティ取得の関数を定数式にすることである。

たとえば、std::numeric_limitsのmax()静的メンバ関数は、int型に対してはINT_MAXマクロの値を返すだけであるが、それが関数であるために、INT_MAXマクロと違って定数として扱えない、という問題があった。抽象化された機能を使うより、抽象化されていない機能の方がよい、というのは、改善すべき事態だった。そのため、関数を静的に評価する仕組みが必要とされた。

また、constexprは、値を計算するテンプレートメタプログラムを置き換えて使用できる。テンプレートメタプログラミングでは、非型テンプレートパラメータによって整数型の値をコンパイル時に計算することはできた。しかし、浮動小数点数型の値や、その他多くの値に関する計算が難しく、構文もまた通常の関数とはかけ離れていた(浮動小数点数型の値の計算は、分数形式にすれば、できることはできる)。値をコンパイル時に計算するためには、今後はテンプレートメタプログラミングよりもconstexprを積極的に使用していくとよいだろう。

関連項目

  • C++11 ユーザー定義リテラル
  • C++14 constexpr関数内での条件分岐とループの文を許可
  • C++17 constexprラムダ
  • C++20 常に定数式評価するconsteval
  • C++20 定数式からの仮想関数の呼び出しを許可
  • C++20 定数式でのdynamic_cast、多態的なtypeidを許可
  • C++20 constexpr関数内でのtry-catchブロックを許可
  • C++20 定数式内での共用体のアクティブメンバの変更を許可
  • C++20 constexpr関数内でのトリビアルなデフォルト初期化を許可
  • C++20 constexpr関数内で未評価のインラインアセンブリを許可することによる組み込み関数のconstexpr有効化
  • C++20 コンパイル時初期化を強制するconstinitキーワードを追加
  • C++20 可変サイズをもつコンテナのconstexpr化
  • C++23 constexpr関数が定数実行できない場合でも適格とする
  • C++23 constexpr関数内でのstatic constexpr変数を許可
  • C++23 constexpr関数内でconsteval関数を呼び出せない問題を軽減
  • C++26 定数評価での例外送出を許可
  • C++26 定数式でのvoid*からポインタ型へのキャストを許可
  • C++26 constexpr配置new
  • C++26 constexpr構造化束縛の許可と、constexpr参照の制限緩和
  • C++26 constexpr仮想継承を許可
  • C++26 共用体の特殊メンバ関数のトリビアル化
  • C++26 コンパイル時にのみ使用される文字列の扱いを明確化

参照

  • N1521 Generalized Constant Expressions
  • N1972 Generalized Constant Expressions — Revision 2
  • N1980 Generalized Constant Expressions — Revision 3
  • N2116 Generalized Constant Expressions — Revision 4
  • N2235 Generalized Constant Expressions — Revision 5
  • CWG Issue 644. Should a trivial class type be a literal type?
    • リテラル型のメンバ変数のみを持つクラスは、constexprコンストラクタを明示的に定義しなくても、リテラル型となる
  • CWG Issue 699. Must constexpr member functions be defined in the class member-specification?
    • ゼロ割りの扱い、再帰回数の規定
  • LWG Issue 2013. Do library implementers have the freedom to add constexpr?
    • C++14で、処理系が規格の要求を超えて標準ライブラリ関数にconstexprを付けることは許可されないと規定された。処理系ごとに定数式で使える関数が異なると、移植性のあるプログラムを書けなくなるため
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.