サーチ…
前書き
constexprは、変数の値を定数式、定数式で潜在的に使用できる関数としてマークするために使用できるキーワード 、または(C ++ 17以降)コンパイルするブランチの1つだけを持つifステートメントをマークします。
備考
constexprキーワードはC ++ 11で追加されましたが、C ++ 11標準が公開されてから数年が経ちましたが、主要なコンパイラではサポートされていませんでした。 C ++ 11標準が公開された時点で。 C ++ 14の発行時点では、すべての主要なコンパイラがconstexprサポートしていconstexpr 。
constexpr変数
constexprと宣言された変数は暗黙的にconstあり、その値は定数式として使用できます。
#define比較
constexprは、 #define基づくコンパイル時の式の代わりにタイプセーフなものです。 constexprすると、コンパイル時に評価される式が結果に置き換えられます。例えば:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
次のコードを生成します:
cout << 12;
プリプロセッサベースのコンパイル時マクロは異なります。検討してください:
#define N 10 + 2
int main()
{
cout << N;
}
生産する:
cout << 10 + 2;
これは明らかにcout << 10 + 2;変換されるcout << 10 + 2; 。しかし、コンパイラはより多くの作業を行う必要があります。また、正しく使用されないと問題が発生します。
たとえば( #define ):
cout << N * 2;
フォーム:
cout << 10 + 2 * 2; // 14
しかし、事前評価されたconstexprは正確に24を与えるだろう。
const比較
const変数は、その記憶にメモリを必要とする変数です 。 constexprはconstexprはありません。 constexprはコンパイル時定数を生成しますが、これは変更できません。あなたはconstが変更されないかもしれないと主張するかもしれません。ただし、
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
ほとんどのコンパイラでは、2番目のステートメントは失敗します(たとえば、GCCで動作する可能性があります)。配列のサイズは、あなたが知っているように、定数式でなければなりません(つまり、コンパイル時の値になります)。 2番目の変数size2は、実行時に決定される値が割り当てられます(コンパイラがコンパイル時に10であることはわかっていますが、コンパイル時ではありません)。
これは、 constが真のコンパイル時定数である場合とそうでない場合があることを意味します。特定のconst値が絶対にコンパイル時であることを保証したり強制することはできません。 #define使用することもできますが、独自の落とし穴があります。
したがって、単に以下を使用してください:
int main()
{
constexpr int size = 10;
int arr[size];
}
constexpr式は、コンパイル時の値に評価されなければなりません。したがって、あなたは使用できません:
constexpr int size = abs(10);
関数( abs )自体がconstexprない限り。
すべての基本型はconstexprで初期化できます。
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
興味深いことに、便利には、 auto使うこともできます:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
constexpr関数
constexprと宣言されたconstexprは暗黙的にインラインであり、そのような関数を呼び出すと潜在的に定数式が得られます。たとえば、次の関数は、定数式の引数で呼び出された場合、定数式も生成されます。
constexpr int Sum(int a, int b)
{
return a + b;
}
したがって、関数呼び出しの結果は、配列のバウンドまたはテンプレートの引数として、またはconstexpr変数を初期化するために使用constexprます。
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
関数の戻り値の型指定からconstexprを削除した場合、 Sはconstexpr変数であるため、 Sへの代入は機能しません。また、コンパイル時constを割り当てる必要があります。同様に、関数Sumがconstexprでない場合、配列のサイズも定数式ではありませconstexpr 。
constexpr関数の興味深い点は、通常の関数のように使うことができることです。
int a = 20;
auto sum = Sum(a, abs(-20));
Sumは現在、 constexpr関数ではなく、通常の関数としてコンパイルされ、可変(非定数)引数をとり、非定数値を返します。 2つの関数を書く必要はありません。
このような呼び出しを非const変数に割り当てると、コンパイルされません:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
理由は簡単です: constexprはコンパイル時定数を割り当てる必要があります。しかし、上記の関数呼び出しは、 Sumを非constexpr (R値は非constですが、L値はconstexprと宣言しています)。
constexpr関数は 、コンパイル時定数も返す必要があります。以下はコンパイルされません。
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
a1は非constexpr 変数であり、関数が真のconstexpr関数ではないconstexprです。それは製造constexpr 、それが割り当ても動作しません-の値は(受信パラメータ)は依然として不明です。 a a
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
さらに、以下もコンパイルされません。
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
abs(a)は定数式ではないのでabs(a) abs(10)も機能しませんabs(10) 、 absはconstexpr int返さないためです!
これはどうですか?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
我々は独自の細工Absある機能constexpr 、との体Absまた、任意のルールを破っていません。また、呼び出しサイト( Sum内)では、式はconstexpr評価されます。したがって、 Sum(-10, 20)の呼び出しはコンパイル時の定数式になり、 30ます。
静的ifステートメント
if constexprステートメントは、条件付きでコードをコンパイルするために使用できます。条件は定数式でなければなりません。選択されていないブランチは破棄されます。テンプレート内の破棄された文はインスタンス化されません。例えば:
template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
さらに、破棄されたステートメント内でのみ使用される変数および関数は定義する必要はなく、破棄されたreturnステートメントは関数の戻り型の控除には使用されません。
if constexprが#ifdefと異なるif constexpr #ifdef条件付きでコードをコンパイルしますが、前処理時に評価できる条件にのみ基づいています。たとえば、テンプレートパラメータの値に応じて#ifdef使用してコードを条件付きでコンパイルすることはできません。一方、構文的に無効なコードを破棄するためにif constexprを使用することはできませんが、 #ifdefは可能です。
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}