サーチ…
前書き
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
}