web-dev-qa-db-ja.com

関数内の静的constexpr変数は意味がありますか?

関数内に変数(たとえば、大きな配列)がある場合、staticconstexprの両方を宣言するのは意味がありますか? constexprは、コンパイル時に配列が作成されることを保証するので、staticは役に立たないでしょうか?

void f() {
    static constexpr int x [] = {
        // a few thousand elements
    };
    // do something with the array
}

staticは、生成されたコードまたはセマンティクスの点で実際に何かをしていますか?

166
David Stone

簡単な答えは、staticが有用であるだけでなく、常に望ましいことです。

まず、staticconstexprは互いに完全に独立していることに注意してください。 staticは、実行中のオブジェクトの有効期間を定義します。 constexprは、コンパイル中にオブジェクトを使用できるように指定します。コンパイルと実行は、時間的にも空間的にもばらばらで不連続です。したがって、プログラムがコンパイルされると、constexprは関係なくなります。

constexprと宣言されたすべての変数は暗黙的にconstですが、conststaticはほぼ直交しています(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を使用したくないでしょう。

197
rici