関数内に変数(たとえば、大きな配列)がある場合、static
とconstexpr
の両方を宣言するのは意味がありますか? constexpr
は、コンパイル時に配列が作成されることを保証するので、static
は役に立たないでしょうか?
void f() {
static constexpr int x [] = {
// a few thousand elements
};
// do something with the array
}
static
は、生成されたコードまたはセマンティクスの点で実際に何かをしていますか?
簡単な答えは、static
が有用であるだけでなく、常に望ましいことです。
まず、static
とconstexpr
は互いに完全に独立していることに注意してください。 static
は、実行中のオブジェクトの有効期間を定義します。 constexpr
は、コンパイル中にオブジェクトを使用できるように指定します。コンパイルと実行は、時間的にも空間的にもばらばらで不連続です。したがって、プログラムがコンパイルされると、constexpr
は関係なくなります。
constexpr
と宣言されたすべての変数は暗黙的にconst
ですが、const
とstatic
はほぼ直交しています(static const
整数との相互作用を除く)。
C++
オブジェクトモデル(§1.9)では、ビットフィールド以外のすべてのオブジェクトが少なくとも1バイトのメモリを占有し、アドレスを持っている必要があります。さらに、特定の瞬間にプログラムで観測可能なすべてのそのようなオブジェクトには、個別のアドレスが必要です(パラグラフ6)。これは、コンパイラがas-if
原則に逃れることができるため、ローカルの非静的const配列で関数を呼び出すたびにスタック上に新しい配列を作成することをまったく必要としません。他のそのようなオブジェクトを観察できます。
残念ながら、関数は自明ではない(たとえば、翻訳単位内で本体が表示されない他の関数を呼び出さない)限り、配列は定義により多かれ少なかれアドレスであるため、それを証明するのは簡単ではありません。そのため、ほとんどの場合、呼び出しのたびに非静的const(expr)
配列をスタック上に再作成する必要があり、コンパイル時に計算できるようになります。
一方、ローカルstatic const
オブジェクトはすべてのオブザーバーによって共有され、さらに、定義されている関数が呼び出されない場合でも初期化される場合があります。したがって、上記のどれも当てはまらず、コンパイラはそのインスタンスを1つだけ生成するだけでなく、自由です。単一のインスタンスを読み取り専用ストレージに自由に生成できます。
したがって、例では間違いなくstatic constexpr
を使用する必要があります。
ただし、static constexpr
を使用したくない場合が1つあります。 constexpr
で宣言されたオブジェクトが ODR-used またはstatic
で宣言されていない限り、コンパイラはそのオブジェクトをまったく含めないようにできます。コンパイルされたプログラムを不要なバイトで汚染することなく、コンパイル時の一時的なconstexpr
配列を使用できるため、これは非常に便利です。その場合、static
は実行時にオブジェクトを強制的に存在させる可能性が高いため、明らかにstatic
を使用したくないでしょう。