constexpr
との違いは何ですかconst
?
- どちらか一方だけを使用できるのはいつですか?
- 両方をいつ使用でき、どのように選択すればよいですか?
ベストアンサー1
基本的な意味と構文
どちらのキーワードも、関数だけでなくオブジェクトの宣言でも使用できます。オブジェクトに適用する場合の基本的な違いは次のとおりです。
const
オブジェクトを定数として宣言します。これは、一度初期化されると、そのオブジェクトの値は変更されないことが保証され、コンパイラはこの事実を最適化に利用できます。また、初期化後に変更されることを意図していないオブジェクトを変更するコードをプログラマが記述するのを防ぐのにも役立ちます。constexpr
標準で定数式と呼ばれるものに使用するのに適したオブジェクトを宣言します。ただし、これがconstexpr
これを行う唯一の方法ではないことに注意してください。
関数に適用する場合の基本的な違いは次のとおりです。
const
非静的メンバー関数にのみ使用でき、一般的な関数には使用できません。メンバー関数が非静的データ メンバーを変更しないことを保証します (変更可能なデータ メンバーは除きます。これらはいずれにしても変更できます)。constexpr
は、メンバー関数と非メンバー関数の両方、およびコンストラクタで使用できます。これは、関数が定数式での使用に適していることを宣言します。コンパイラは、関数が特定の基準 (7.1.5/3,4) を満たしている場合にのみこれを受け入れます。最も重要なのは(†)です。- 関数本体は非仮想かつ非常に単純である必要があります。typedef と静的アサートを除き、単一の
return
ステートメントのみが許可されます。コンストラクターの場合、初期化リスト、typedef、および静的アサートのみが許可されます。(ただし、= default
も= delete
許可されます。) - C++14 以降では、ルールが緩和され、constexpr 関数内で許可される内容は、
asm
宣言、文、およびgoto
以外のラベルを持つ文、try ブロック、非リテラル型の変数の定義、静的またはスレッド ストレージ期間の変数の定義、初期化が実行されない変数の定義です。case
default
- 引数と戻り値の型はリテラル型(つまり、一般的には非常に単純な型、通常はスカラーまたは集合体)である必要があります。
- 関数本体は非仮想かつ非常に単純である必要があります。typedef と静的アサートを除き、単一の
定数式
上で述べたように、constexpr
オブジェクトと関数の両方を定数式での使用に適したものとして宣言します。定数式は単なる定数以上のものです。
テンプレートパラメータや配列サイズ指定子など、コンパイル時の評価が必要な場所で使用できます。
template<int N> class fixed_size_list { /*...*/ }; fixed_size_list<X> mylist; // X must be an integer constant expression int numbers[X]; // X must be an integer constant expression
ただし、注意してください:
何かを として宣言しても、
constexpr
必ずしもコンパイル時に評価されることが保証されるわけではありません。そのような用途には使用できますが、実行時に評価される他の場所でも使用できます。オブジェクトは、宣言されなくても定数式で使用できる場合があります。例:
constexpr
int main() { const int N = 3; int numbers[N] = {1, 2, 3}; // N is constant expression }
これが可能なのは
N
、 が定数であり、宣言時にリテラルで初期化されるため、 が宣言されていなくても定数式の基準を満たすためですconstexpr
。
では、実際にいつ使用する必要があるのでしょうかconstexpr
?
- 上記のようなオブジェクトは、宣言せずに
N
定数式として使用できます。これは、次のすべてのオブジェクトに当てはまります。constexpr
const
そして- 整数型または列挙型で
- 宣言時に、それ自体が定数式である式で初期化されます。
[これは、§5.19/2 によるものです: 定数式には、「[…] 整数型または列挙型の glvalue でない限り、lvalue から rvalue への変更」を含む部分式を含めることはできません。これはすべてのリテラル型に当てはまるという私の以前の主張を訂正してくれた Richard Smith に感謝します。
関数を定数式で使用できるようにするためには、明示的に宣言する必要があります
constexpr
。定数式関数の基準を満たすだけでは十分ではありません。例:template<int N> class list { }; constexpr int sqr1(int arg) { return arg * arg; } int sqr2(int arg) { return arg * arg; } int main() { const int X = 2; list<sqr1(X)> mylist1; // OK: sqr1 is constexpr list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr }
const
両方を一緒に使用できる場合、または使用すべきなのはいつですかconstexpr
?
A. オブジェクト宣言内。両方のキーワードが宣言される同じオブジェクトを参照する場合、これは必要ありません。constexpr
を意味しますconst
。
constexpr const int N = 5;
と同じです
constexpr int N = 5;
ただし、キーワードがそれぞれ宣言の異なる部分を参照する場合があることに注意してください。
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
NP
ここで、 はアドレス定数式、つまりそれ自体が定数式であるポインターとして宣言されています。 (これは、アドレス演算子を静的/グローバル定数式に適用することによってアドレスが生成される場合に可能です。) ここでは、 と の両方がconstexpr
必要const
です。constexpr
は常に宣言されている式 (ここではNP
) を参照しますが、const
は を参照しますint
(これは const へのポインターを宣言します)。 を削除すると、const
式が不正になります ((a) 非 const オブジェクトへのポインターは定数式にはならず、(b) は&N
実際には定数へのポインターであるため)。
B. メンバー関数の宣言。C ++11ではconstexpr
を意味しますがconst
、C++14とC++17ではそうではありません。C++11で次のように宣言されたメンバー関数は、
constexpr void f();
宣言する必要がある
constexpr void f() const;
C++14 では関数として使用できるようにするためconst
。