web-dev-qa-db-ja.com

構造体内の静的constexpr変数とクラスの初期化

これが私の作業コードの例です:

#include <iostream>

template<typename B>
class b {
public:
    int y;

    constexpr b(int x) : y(x) {

    }

    constexpr void sayhi() {
        std::cout << "hi" << std::endl;
    }
};



template<int x>
struct A {
    static constexpr b<int> bee = x;
    static constexpr int y = x;         // this one is fine and usable already, I don't have to do something like what I did on member bee

    inline static void sayhi() {
        std::cout << y << std::endl;
    }
};

template<int x>
constexpr b<int> A<x>::bee;        // why do I have to do something like this, it will cause errors if I don't.

int main(int argc, char** argv) {
    A<30>::bee.sayhi();             // works fine
    A<30>::sayhi();                 // works fine

    return 0;
}

私のコードは単純です。2つの静的変数、つまりstatic constexpr int ystatic constexpr b<int> bee = x;を持つテンプレートstructAがあります。私のテンプレート構造体Aは、テンプレートパラメータからxによってコピーされる引数の値を取得します。私の質問は、クラスに関しては、次のようなことをしてクラスを初期化する必要があるのです。

template<int x>
constexpr b<int> A<x>::bee; 

上記のコードを使用しないと、undefined参照エラーが発生します。ここで、intはすでに問題なく、次のようなことをするだけでアクセスできます。

static constexpr int y = x;    

なぜこれ以上前方宣言する必要がないのか心配です。

11

static constexprメンバーは、初期化時にclass { }スコープ内で値を持ちますが、class { }の外で定義されるまで、メモリ内の場所(アドレス)を持ちません。その理由は、そのスペシャライゼーションの一部またはすべてをリンクライブラリ(例:.oまたは.so)に含めるか、デフォルトでスペシャライゼーションに効果的にインラインリンクを与えるかどうかを決定できるためです。

オブジェクトのアドレスを使用する場合は、クラス外の定義が必要です。これは、オブジェクトがグローバル変数として存在する必要があることを意味します。一方、constexprメンバーをコンパイル時にのみ存在させ、グローバルストレージの割り当てを禁止する場合は、定義を省略することをお勧めします。

ちなみに、std::coutに出力されるconstexprなど、定数式として評価できない関数にsayhi指定子を配置することは許可されていません。これは「診断不要(NDR)」ルールです。つまり、コンパイラは今は文句を言わないかもしれませんが、次のコンパイラバージョンは文句を言うかもしれません。

10
Potatoswatter