これが私の作業コードの例です:
#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 y
とstatic constexpr b<int> bee = x;
を持つテンプレートstructA
があります。私のテンプレート構造体A
は、テンプレートパラメータからx
によってコピーされる引数の値を取得します。私の質問は、クラスに関しては、次のようなことをしてクラスを初期化する必要があるのです。
template<int x>
constexpr b<int> A<x>::bee;
上記のコードを使用しないと、undefined
参照エラーが発生します。ここで、intはすでに問題なく、次のようなことをするだけでアクセスできます。
static constexpr int y = x;
なぜこれ以上前方宣言する必要がないのか心配です。
static constexpr
メンバーは、初期化時にclass { }
スコープ内で値を持ちますが、class { }
の外で定義されるまで、メモリ内の場所(アドレス)を持ちません。その理由は、そのスペシャライゼーションの一部またはすべてをリンクライブラリ(例:.o
または.so
)に含めるか、デフォルトでスペシャライゼーションに効果的にインラインリンクを与えるかどうかを決定できるためです。
オブジェクトのアドレスを使用する場合は、クラス外の定義が必要です。これは、オブジェクトがグローバル変数として存在する必要があることを意味します。一方、constexpr
メンバーをコンパイル時にのみ存在させ、グローバルストレージの割り当てを禁止する場合は、定義を省略することをお勧めします。
ちなみに、std::cout
に出力されるconstexpr
など、定数式として評価できない関数にsayhi
指定子を配置することは許可されていません。これは「診断不要(NDR)」ルールです。つまり、コンパイラは今は文句を言わないかもしれませんが、次のコンパイラバージョンは文句を言うかもしれません。