変数に対するconstとconstexpr


303

次の定義に違いはありますか?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

そうでない場合、C ++ 11ではどのスタイルが推奨されますか?



どちらもコンパイル時定数です。ただし、最初のconst_castを実行して書き込むことができます。ただし、コンパイル時に発生する「読み取り」には影響しないため、コンパイラによって最適化されます。
Bonita Montero

回答:


347

違いがあると思います。それらについてより簡単に説明できるように、名前を変更しましょう。

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

PI1PI2はどちらも定数です。つまり、変更できません。ただし、コンパイル時の定数のみ PI2です。これは、もの、コンパイル時に初期化されます。 PI1コンパイル時または実行時に初期化されます。さらに、コンパイル時定数を必要とするコンテキストでのみ PI2使用できます。例えば:

constexpr double PI3 = PI1;  // error

だが:

constexpr double PI3 = PI2;  // ok

そして:

static_assert(PI1 == 3.141592653589793, "");  // error

だが:

static_assert(PI2 == 3.141592653589793, "");  // ok

あなたはどちらを使うべきですか?ニーズに合った方を使用してください。コンパイル時定数が必要なコンテキストで使用できるコンパイル時定数があることを確認しますか?実行時に行われる計算で初期化できるようにしますか?等。


60
本気ですか?そのためconst int N = 10; char a[N];作品、および配列の境界は、コンパイル時定数でなければなりません。
fredoverflow

10
私が書いた例は(投稿する前にそれぞれをテストした)限り、確実です。ただし、私のコンパイラではPI1、配列で使用するためにコンパイル時の積分定数に変換できますが、型以外の積分テンプレートパラメータとしては使用できません。したがって、コンパイル時PI1の整数型への変換可能性は、私には少し当たり前のように思えます。
ハワードヒナン

34
@FredOverflow:非const配列インデックスは約10年間「機能しました」(たとえば、そのためのg ++​​拡張があります)が、それが厳密に合法なC ++であることを意味するわけではありません(ただし、最近のCまたはC ++標準によって合法化されましたが、どちらを忘れたか)。コンパイル時定数、テンプレートパラメータや、使用の違いほどenum初期化子が間に2つだけの顕著な違いですconstconstexpr(そしてどちらの作品doubleとにかく)。
デイモン

17
5.19の第4段落定数式[expr.const]も(非規範的)な注記であり、実装時と実行時のコンパイル時の浮動小数点演算の実行が(たとえば、精度に関して)異なることは有名です。したがって1 / PI11 / PI2異なる結果が生じる可能性があります。この専門性は、この回答のアドバイスほど重要ではないと思います。
Luc Danton、

4
しかし、それconstexpr double PI3 = PI1;は私にとっては正しく機能します。(MSVS2013 CTP)。何が悪いのですか?
NuPagadi 2014年

77

ここでは違いはありませんが、コンストラクターを持つ型がある場合は重要です。

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0定数ですが、コンパイル時に初期化されるとは限りません。s1はマークされているconstexprため、定数であり、Sのコンストラクタもマークさconstexprれているため、コンパイル時に初期化されます。

これは、実行時の初期化に時間がかかり、その作業をコンパイラーにプッシュしたい場合に最も重要です。コンパイラーにも時間がかかりますが、コンパイルされたプログラムの実行時間は遅くなりません。


3
私が同意する:私が到達した結論constexprは、オブジェクトのコンパイル時の計算が不可能である場合、診断につながるということでした。あまり明確ではないのは、定数パラメーターを期待する関数が、次のようにではconstなくとして宣言された場合に、コンパイル時に実行できるかどうかです。constexprつまり、constexpr int foo(S)呼び出し時にコンパイル時に実行されますfoo(s0)か?
Matthieu M.

4
@MatthieuM:foo(s0)コンパイル時に実行されるかどうかは疑問ですが、あなたは決して知りません。コンパイラーはそのような最適化を行うことが許可されています。確かに、gcc 4.7.2もclang 3.2もコンパイルできませんconstexpr a = foo(s0);
rici

50

constexprは定数であり、コンパイル時に既知の値を示します。
constは定数のみの値を示します。コンパイル中に知っておく必要はありません。

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

constオブジェクトはコンパイル中に既知の値で初期化する必要がないため、constはconstexprと同じ保証を提供しないことに注意してください。

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

すべてのconstexprオブジェクトはconstですが、すべてのconstオブジェクトがconstexprであるとは限りません。

変数に、コンパイル時の定数を必要とするコンテキストで使用できる値が含まれていることをコンパイラーに保証したい場合、到達するツールはconstではなくconstexprです。


2
私はあなたの説明がとても気に入りました。実際のシナリオでコンパイル時定数を使用する必要があるケースはどこにあるかについてコメントしてください。
Mayukh Sarkar

1
@MayukhSarkar単にGoogle C ++でconstexprを使用する理由。例:stackoverflow.com/questions/4748083/…–
underscore_d

18

constexprのシンボリック定数は、コンパイル時に知られている値を与えなければなりません。例えば:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

コンパイル時には不明であるが初期化後には変更されない値で初期化される「変数」の値を処理するために、C ++は2番目の形式の定数(const)を提供します。例えば:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

このような「定数変数」は、次の2つの理由で非常に一般的です。

  1. C ++ 98にはconstexprがなかったため、人々はconstを使用していました。
  2. 定数式ではありませんが(コンパイル時にその値は不明です)、初期化後に値を変更しないリスト項目「変数」は、それ自体が広く役立ちます。

参照:Stroustrupによる「プログラミング:C ++を使用した原則と実践」


25
おそらく、回答のテキストはStroustrupの「プログラミング:C ++を使用した原則と実践」の逐語的解釈である
Aky
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.