constexpr
とconst
の違いは何ですか?
両方のキーワードは、オブジェクトと関数の宣言で使用できます。 objectsに適用した場合の基本的な違いは次のとおりです。
const
は、オブジェクトをconstantとして宣言します。これは、初期化された後、そのオブジェクトの値が変わらないという保証を意味し、コンパイラは最適化のためにこの事実を利用できます。また、初期化後に変更されることを意図していないオブジェクトを変更するコードをプログラマが作成するのを防ぐのにも役立ちます。
constexpr
は、Standardが呼び出すものに使用するのに適したオブジェクトを宣言します定数式。しかし、constexpr
がこれを行う唯一の方法ではないことに注意してください。
functionsに適用される場合、基本的な違いは次のとおりです。
const
は、一般的な関数ではなく、非静的メンバー関数にのみ使用できます。メンバー関数が非静的データメンバーを変更しないことを保証します。
constexpr
は、メンバー関数と非メンバー関数の両方、およびコンストラクターで使用できます。 定数式での使用に適した関数を宣言します。コンパイラは、関数が特定の基準(7.1.5/3,4)を満たしている場合にのみ受け入れます。最も重要なこと (†):
return
ステートメントは1つだけ許可されます。コンストラクターの場合、初期化リスト、typedef、および静的アサートのみが許可されます。 (= default
と= delete
も許可されています。)asm
宣言、goto
ステートメント、case
以外のラベルを持つステートメントdefault
、try-block、非リテラル型の変数の定義、静的またはスレッド保存期間の変数の定義、初期化が実行されない変数の定義。上記のように、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
として宣言しても、必ずしもコンパイル時に評価されるとは限りません。 使用可能などですが、実行時に評価される他の場所でも使用できます。
オブジェクトmay定数式での使用に適合withoutconstexpr
と宣言されます。例:
int main()
{
const int N = 3;
int numbers[N] = {1, 2, 3}; // N is constant expression
}
定数であり、宣言時にリテラルで初期化されるN
は、constexpr
として宣言されていなくても、定数式の基準を満たすため、これが可能です。
だからいつ実際にconstexpr
を使わなければならないのか?
上記のN
のようなobjectは、定数式として使用できますwithoutconstexpr
と宣言されます。これは、次のすべてのオブジェクトに当てはまります。
const
[これは§5.19/ 2によるものです。定数式には、[…]整数型または列挙型のglvalue […]以外の左辺値から右辺値への変更を含む部分式を含めることはできません。以前は、これはすべてのリテラル型に当てはまると主張していました。]
functionが定数式での使用に適合するためには、must明示的に宣言される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
together?
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;
const
関数として引き続き使用できるようにするために、C++ 14の下で。
const
は 変数 に適用され、 はコード内での変更を防止します 。
constexpr
は、この expression が コンパイル時定数値 になることをコンパイラーに指示するので、配列長、const
変数への代入などの場所で使用できます。 link オリによって与えられた多くの優れた例があります。
基本的にそれらは完全に2つの異なる概念であり、一緒に使用することができます(そしてそうすべきです)。
const
はプログラム がオブジェクトの値 を変更しないことを保証します。ただし、const
は、オブジェクトがどのタイプの初期化を受けるかを保証しません。
検討してください:
const int mx = numeric_limits<int>::max(); // OK: runtime initialization
関数max()
は単にリテラル値を返します。ただし、初期化子は関数呼び出しであるため、mx
はランタイム初期化を受けます。したがって、定数式として使用することはできません。
int arr[mx]; // error: “constant expression required”
constexpr
は、C++ 11の新しいキーワードで、マクロとハードコードされたリテラルを作成する必要がありません。また、特定の条件下で、オブジェクトがstatic初期化を受けることを保証します。式の評価時間を制御します。 その式のコンパイル時評価 を強制することによって、constexpr
は、タイムクリティカルなアプリケーション、システムプログラミング、テンプレート、そして一般的に言えば、依存するすべてのコードにおいて重要な真のconstant式を定義します。コンパイル時定数.
定数式関数はconstexpr
として宣言された関数です。その本体は非仮想でなければならず、typedefやstatic assertとは別に、単一のreturn文だけで構成されていなければなりません。その引数と戻り値はリテラル型でなければなりません。これは非定数式の引数と共に使用できますが、それが行われたときの結果は定数式ではありません。
定数式関数は、パフォーマンスや型の安全性を犠牲にすることなく、macrosとhardcoded literalsを置き換えることを目的としています。
constexpr int max() { return INT_MAX; } // OK
constexpr long long_max() { return 2147483647; } // OK
constexpr bool get_val()
{
bool res = false;
return res;
} // error: body is not just a return statement
constexpr int square(int x)
{ return x * x; } // OK: compile-time evaluation only if x is a constant expression
const int res = square(5); // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y); // OK: runtime evaluation of square(y)
constant-expression objectはconstexpr
と宣言されたオブジェクトです。定数式、または定数式引数を持つ定数式コンストラクターによって構成された右辺値で初期化する必要があります。
定数式オブジェクトは、使用前に初期化が必要で、初期化子が定数式でなければならないことを除いて、const
として宣言されているかのように動作します。したがって、定数式オブジェクトは常に別の定数式の一部として使用できます。
struct S
{
constexpr int two(); // constant-expression function
private:
static constexpr int sz; // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
Small = S::two(), // error: S::two() called before it was defined
Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()]; // OK: s.two() called after its definition
constant-expressionコンストラクタは、constexpr
と宣言されたコンストラクタです。メンバー初期化リストを持つことができますが、typedefや静的アサーションとは別に、その本体は空でなければなりません。その引数はリテラル型を持たなければなりません。
コンストラクタの引数がすべて定数式である場合、定数式コンストラクタを使用すると、コンパイラはコンパイル時にオブジェクトを初期化できます。
struct complex
{
// constant-expression constructor
constexpr complex(double r, double i) : re(r), im(i) { } // OK: empty body
// constant-expression functions
constexpr double real() { return re; }
constexpr double imag() { return im; }
private:
double re;
double im;
};
constexpr complex COMP(0.0, 1.0); // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0); // error: x is not a constant expression
const complex cx2(x, 1); // OK: runtime initialization
constexpr double xx = COMP.real(); // OK: compile-time initialization
constexpr double imaglval = COMP.imag(); // OK: compile-time initialization
complex cx3(2, 4.6); // OK: runtime initialization
constexpr
についてのScott MeyersによるEffective Modern C++からのヒント:
constexpr
オブジェクトはconstであり、コンパイル中に既知の値で初期化されます。constexpr
関数は、コンパイル時に値がわかっている引数を指定して呼び出されると、コンパイル時に結果を生成します。constexpr
オブジェクトおよび関数は、非constexpr
オブジェクトおよび関数よりも広範囲のコンテキストで使用できます。constexpr
は、オブジェクトまたは関数のインターフェースの一部です。Bjarne Stroustrupによる「C++プログラミング言語第4編集」の本によると
• const :「この値は変更しないことをお約束します」(7.5)を意味します。これは主にインターフェースを指定するために使用されるので、データが変更されることを恐れずにデータを関数に渡すことができます。
コンパイラは、constによる約束を守ります。
• constexpr :大体「コンパイル時に評価される」という意味です(10.4)。これは主に定数を指定するために使われます。
例えば:
const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression
関数を定数式、つまりコンパイラによって評価される式で使用できるようにするには、 constexpr を定義する必要があります。
例えば:
constexpr double square(double x) { return x∗x; }
constexprになるためには、関数はかなり単純でなければなりません:単に値を計算するreturnステートメントです。 constexpr関数は、定数でない引数に使用できますが、それが行われたときの結果は定数式ではありません。定数式を必要としないコンテキストでは、定数式以外の引数を使ってconstexpr関数を呼び出すことができます。そのため、同じ式を2回定義する必要はありません。
いくつかの場所では、定数式は言語の規則(例えば、配列の範囲(2.2.5、§7.3)、ケースラベル(2.2.2、§9.4.2)、いくつかのテンプレート引数(例: §25.2)、およびconstexprを使って宣言された定数)それ以外の場合、コンパイル時の評価はパフォーマンスにとって重要です。性能の問題とは無関係に、不変性(不変の状態をもつオブジェクト)の概念は、設計上の重要な事項です(§10.4)。
const
とconstexpr
はどちらも変数と関数に適用できます。それらは互いに似ていますが、実際には非常に異なる概念です。
const
とconstexpr
はどちらも、初期化後に値を変更できないことを意味します。だから、例えば:
const int x1=10;
constexpr int x2=10;
x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.
const
とconstexpr
の主な違いは、それらの初期化値がわかっている(評価される)時間です。 const
変数の値はコンパイル時と実行時の両方で評価できますが、constexpr
は常にコンパイル時に評価されます。例えば:
int temp=Rand(); // temp is generated by the the random generator at runtime.
const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.
値がコンパイル時または実行時にわかっているかどうかを知ることの重要な利点は、コンパイル時定数が必要なときはいつでもコンパイル時定数を使用できるという事実です。たとえば、C++では、可変長のC配列を指定することはできません。
int temp=Rand(); // temp is generated by the the random generator at runtime.
int array1[10]; // OK.
int array2[temp]; // ERROR.
つまり、ということです。
const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.
int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.
そのためconst
変数は両方を定義できます 時定数をコンパイルする 配列サイズを指定するために使用できるsize1
のように 実行時定数 size2
のように、実行時にだけ知られていて、配列サイズを定義するのに使用することができません。一方、constexpr
は常に配列サイズを指定できるコンパイル時定数を定義します。
const
とconstexpr
はどちらも関数にも適用できます。 const
キーワードは、const
キーワードを適用してもメソッドがそのメンバ(非静的)フィールドの値を変更できないことを意味するメンバ関数(メソッド、演算子)である必要があります。例えば。
class test
{
int x;
void function1()
{
x=100; // OK.
}
void function2() const
{
x=100; // ERROR. The const methods can't change the values of object fields.
}
};
constexpr
は別の概念です。それはコンパイル時に評価されることができる関数として関数(メンバまたは非メンバ)をマークします コンパイル時定数が引数として渡された場合。例えばこれを書くことができます。
constexpr int func_constexpr(int X, int Y)
{
return(X*Y);
}
int func(int X, int Y)
{
return(X*Y);
}
int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.
int array3[func_constexpr(10,Rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,Rand())' can't be evaluated at compile time.
ちなみにconstexpr
関数は、定数でない引数が渡された場合でも呼び出すことができる通常のC++関数です。しかしその場合、あなたはnon-constexpr値を得ています。
int value1=func_constexpr(10,Rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,Rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,Rand()) can't be evaluated at compile time.
constexpr
はメンバ関数(メソッド)、演算子そしてコンストラクタにも適用できます。例えば。
class test2
{
static constexpr int function(int value)
{
return(value+1);
}
void f()
{
int x[function(10)];
}
};
もっと 'クレイジーな'サンプル。
class test3
{
public:
int value;
// constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
constexpr int getvalue() const
{
return(value);
}
constexpr test3(int Value)
: value(Value)
{
}
};
constexpr test3 x(100); // OK. Constructor is constexpr.
int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.
@ 0x499602d2がすでに指摘したように、const
は初期化後に値が変更できないことを保証するだけです。ここでconstexpr
(C++ 11で導入された)は変数がコンパイル時定数であることを保証します。
(LearnCpp.comの)次の例を考えてください。
cout << "Enter your age: ";
int age;
cin >> age;
const int myAge{age}; // works
constexpr int someAge{age}; // error: age can only be resolved at runtime
const int var
は実行時に動的に値に設定することができ、一度その値に設定されるとそれ以上変更することはできません。
constexpr int var
は実行時に動的に設定することはできませんが、むしろコンパイル時に設定します。そして一度それがその値に設定されると、それはもはや変更できません。
これは堅実な例です。
int main(int argc, char*argv[]) {
const int p = argc;
// p = 69; // cannot change p because it is a const
// constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time
constexpr int r = 2^3; // this works!
// r = 42; // same as const too, it cannot be changed
}
上記のスニペットはうまくコンパイルされていて、エラーの原因となっているものはコメントアウトしています。
まず第一に、どちらもc ++の修飾子です。 constで宣言された変数は初期化する必要があり、将来変更することはできません。したがって、通常、constとして宣言された変数は、コンパイル前でも値を持ちます。
ただし、constexprの場合は少し異なります。
Constexprの場合、プログラムのコンパイル中に評価できる式を指定できます。
明らかに、constexperとして宣言された変数は、constのように将来変更することはできません。