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


192

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

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

されstatic、実際に生成されたコードや意味論の面でそこに何かをやって?

回答:


230

簡単に言えば、static便利であるだけでなく、常に望ましいことです。

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

宣言されたすべての変数constexprは暗黙的ですconstconststaticほぼ直交しています(static const整数との相互作用を除く)。

C++オブジェクトモデル(§1.9)は、すべてのビットフィールドは、メモリの少なくとも1つのバイトを占有し、アドレスを持っている以外のオブジェクトことを必要とします。さらに、特定の瞬間にプログラムで観察可能なすべてのそのようなオブジェクトは、別個のアドレスを持つ必要があります(パラグラフ6)。これは、ローカルの非静的const配列を使用して関数を呼び出すたびに、スタックに新しい配列を作成することをコンパイラーに要求するものではありません。これはas-if、そのようなオブジェクトが他にないことを証明できれば、コンパイラーは原則を逃れる可能性があるためです。観察された。

残念ながら、関数が自明でない限り(たとえば、本体が変換単位内に表示されない他の関数を呼び出さない)、配列は定義によりアドレスになっているため、これを証明するのは簡単ではありません。そのため、ほとんどの場合、非静的const(expr)配列は呼び出しのたびにスタック上に再作成する必要があり、コンパイル時にそれを計算できるという点がなくなります。

一方、ローカルstatic constオブジェクトはすべてのオブザーバーによって共有され、さらに、それが定義されている関数が呼び出されない場合でも初期化されることがあります。したがって、上記のいずれも当てはまりません。コンパイラーは、そのインスタンスを1つだけ生成するだけでなく、単一のインスタンスを読み取り専用ストレージに自由に生成できます。

だからあなたは間違いなくstatic constexprあなたの例で使うべきです。

ただし、を使用したくない場合がありますstatic constexpr。ない限り、constexpr宣言されたオブジェクトは、どちらかであるODR-使用または宣言はstatic、コンパイラがすべてでそれを含まないように自由です。コンパイルconstexprされたプログラムを不要なバイトで汚染することなく、コンパイル時の一時配列を使用できるため、これは非常に便利です。その場合、は実行時にオブジェクトを強制的に存在させる可能性があるためstatic、を使用したくないことは明らかですstatic


2
@AndrewLazarus、オブジェクトconstから離れてキャストすることはできません。を指すconstからのみキャストできます。しかし、それは重要ではありません。ポイントは、自動オブジェクトが静的アドレスを持つことができないことです。私は、言ったようにコンパイルが終了すると有意義でなくなる(オブジェクトがさえ、実行時に存在することが保証されていないので、すべての恐らく何も、。)、そう離れてキャストには何もありませんconst X*Xconstexpr
RICI

17
この答えは信じられないほど混乱しているだけでなく、自己矛盾しているようにも感じます。たとえば、あなたはほとんど常に欲しいstaticと言っていますconstexprが、それらは直交していて独立しており、異なることをしています。次に、ODRの使用を無視するため、この2つを組み合わせない理由を述べます(これは便利なようです)。静的は実行時のものなので、なぜconstexprで静的を使用する必要があるのか​​はまだわかりません。constexprでstaticが重要である理由を説明したことはありません。
void.pointer 2014年

2
@ void.pointer:あなたは最後の段落について正しいです。イントロを変更しました。static constexpr(定数配列をすべての関数呼び出しで再作成する必要がないようにすること)の重要性を説明したと思いましたが、わかりやすくするためにいくつかの単語を微調整しました。ありがとう。
rici 2014年

8
コンパイル時の定数と実行時の定数の比較にも役立つでしょう。言い換えると、constexpr定数変数がコンパイル時のコンテキストでのみ使用され、実行時に必要とされない場合staticは、意味がありません。実行時に到達した時点で、値は実質的に「インライン化」されているためです。ただし、constexprがランタイムコンテキストで使用される場合(つまり、constexprconst暗黙的に変換され、ランタイムコードの物理アドレスで使用可能である必要がある場合)static、ODRコンプライアンスなどを確保する必要があります。少なくとも、私の理解です。
void.pointer 2015年

3
私の最後のコメントの例:static constexpr int foo = 100;。コードがのようなものでない限り、コンパイラーがfooリテラルの代わりにあらゆる場所の使用法を代用できなかった理由はありません。だから、上から、この場合には有用性を持っていない、実行時には存在しません。繰り返しになりますが、コンパイラ次第です。100&foostaticfoofoo
void.pointer 2015年

10

与えられた答えに加えて、そのコンパイラに注意することの価値を初期化するために必要とされていないconstexprとの差異ことを知って、コンパイル時に変数constexprとは、static constexprそれが使用することですstatic constexprあなたは変数が一度だけ初期化されていることを確認します。

次のコードは、constexpr変数が(同じ値でも)複数回初期化される方法を示していますが、static constexpr確実に1回だけ初期化されています。

さらに、コードはconstexprconstと組み合わせた場合のに対する利点を比較しstaticます。

#include <iostream>
#include <string>
#include <cassert>
#include <sstream>

const short const_short = 0;
constexpr short constexpr_short = 0;

// print only last 3 address value numbers
const short addr_offset = 3;

// This function will print name, value and address for given parameter
void print_properties(std::string ref_name, const short* param, short offset)
{
    // determine initial size of strings
    std::string title = "value \\ address of ";
    const size_t ref_size = ref_name.size();
    const size_t title_size = title.size();
    assert(title_size > ref_size);

    // create title (resize)
    title.append(ref_name);
    title.append(" is ");
    title.append(title_size - ref_size, ' ');

    // extract last 'offset' values from address
    std::stringstream addr;
    addr << param;
    const std::string addr_str = addr.str();
    const size_t addr_size = addr_str.size();
    assert(addr_size - offset > 0);

    // print title / ref value / address at offset
    std::cout << title << *param << " " << addr_str.substr(addr_size - offset) << std::endl;
}

// here we test initialization of const variable (runtime)
void const_value(const short counter)
{
    static short temp = const_short;
    const short const_var = ++temp;
    print_properties("const", &const_var, addr_offset);

    if (counter)
        const_value(counter - 1);
}

// here we test initialization of static variable (runtime)
void static_value(const short counter)
{
    static short temp = const_short;
    static short static_var = ++temp;
    print_properties("static", &static_var, addr_offset);

    if (counter)
        static_value(counter - 1);
}

// here we test initialization of static const variable (runtime)
void static_const_value(const short counter)
{
    static short temp = const_short;
    static const short static_var = ++temp;
    print_properties("static const", &static_var, addr_offset);

    if (counter)
        static_const_value(counter - 1);
}

// here we test initialization of constexpr variable (compile time)
void constexpr_value(const short counter)
{
    constexpr short constexpr_var = constexpr_short;
    print_properties("constexpr", &constexpr_var, addr_offset);

    if (counter)
        constexpr_value(counter - 1);
}

// here we test initialization of static constexpr variable (compile time)
void static_constexpr_value(const short counter)
{
    static constexpr short static_constexpr_var = constexpr_short;
    print_properties("static constexpr", &static_constexpr_var, addr_offset);

    if (counter)
        static_constexpr_value(counter - 1);
}

// final test call this method from main()
void test_static_const()
{
    constexpr short counter = 2;

    const_value(counter);
    std::cout << std::endl;

    static_value(counter);
    std::cout << std::endl;

    static_const_value(counter);
    std::cout << std::endl;

    constexpr_value(counter);
    std::cout << std::endl;

    static_constexpr_value(counter);
    std::cout << std::endl;
}

可能なプログラム出力:

value \ address of const is               1 564
value \ address of const is               2 3D4
value \ address of const is               3 244

value \ address of static is              1 C58
value \ address of static is              1 C58
value \ address of static is              1 C58

value \ address of static const is        1 C64
value \ address of static const is        1 C64
value \ address of static const is        1 C64

value \ address of constexpr is           0 564
value \ address of constexpr is           0 3D4
value \ address of constexpr is           0 244

value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0

ご覧のとおり、自分constexprは複数回static初期化されています(アドレスは同じではありません)一方で、キーワードは初期化が1回だけ実行されることを保証します。


constexpr_shortが再度初期化されたconstexpr const short constexpr_short場合にエラーを表示するために使用できないのか
akhileshzmishra

の構文はconstexpr const意味がありません。constexprすでにあるため、コンパイラーは1回または複数回のconst追加をconst無視します。エラーをキャッチしようとしていますが、これはエラーではありません。これがほとんどのコンパイラの動作方法です。
メタブラスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.