クラス内の非 conststatic
メンバーまたは配列を初期化できないのはなぜですか?static
class A
{
static const int a = 3;
static int b = 3;
static const int c[2] = { 1, 2 };
static int d[2] = { 1, 2 };
};
int main()
{
A a;
return 0;
}
コンパイラは次のエラーを発行します。
g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member ‘b’
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type ‘const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type ‘int [2]’
質問が2つあります。
static
クラスでデータ メンバーを初期化できないのはなぜですか?static
なぜ配列であってもクラス内で配列を初期化できないのでしょうかconst
?
ベストアンサー1
static
クラスでデータ メンバーを初期化できないのはなぜですか?
a
C++ 標準では、クラス内で初期化できるのは静的定数、整数、または列挙型のみです。これが、 は初期化できるが、他の型は初期化できない理由です。
参照:
C++03 9.4.2 静的データメンバー
§4
静的データ メンバーが const 整数型または const 列挙型の場合、クラス定義内のその宣言では、定数初期化子を指定できます。定数初期化子は、整数定数式 (5.19) でなければなりません。その場合、メンバーは整数定数式に出現できます。メンバーは、プログラムで使用される場合、名前空間スコープで定義され、名前空間スコープ定義には初期化子が含まれてはなりません。
整数型とは何ですか?
C++03 3.9.1 基本型
§7
bool、char、wchar_t型、符号付き整数型、符号なし整数型を総称して整数型と呼びます。43) 整数型の同義語は整数型です。
脚注:
43)したがって、列挙型(7.2)は整数ではない。ただし、列挙型は、4.5で指定されているように、int、unsigned int、long、またはunsigned longに昇格することができる。
回避策:
あなたは列挙トリッククラス定義内で配列を初期化します。
class A
{
static const int a = 3;
enum { arrsize = 2 };
static const int c[arrsize] = { 1, 2 };
};
なぜ標準ではこれが許可されないのでしょうか?
ビャルネはこれを適切に説明しているここ:
クラスは通常、ヘッダー ファイルで宣言され、ヘッダー ファイルは通常、多くの翻訳単位にインクルードされます。ただし、複雑なリンカー ルールを回避するために、C++ ではすべてのオブジェクトに一意の定義が必要です。オブジェクトとしてメモリに格納する必要があるエンティティのクラス内定義を C++ で許可すると、このルールが破られることになります。
static const
クラス内初期化で整数型と列挙型のみが許可されるのはなぜですか?
答えはビャルネの引用文の中に隠されています。よく読んでみてください。
「C++ では、すべてのオブジェクトが一意の定義を持つことが求められます。オブジェクトとしてメモリに格納する必要があるエンティティのクラス内定義を C++ が許可すると、このルールは破られることになります。」
コンパイル時定数として扱えるのは整数のみであることに注意してくださいstatic const
。コンパイラは整数値がいつでも変更されないことを認識しているため、独自のマジックを適用して最適化を適用できます。コンパイラは、そのようなクラス メンバーを単にインライン化します。つまり、それらはメモリに格納されなくなります。メモリに格納する必要がなくなるため、そのような変数には Bjarne が言及したルールの例外が与えられます。
ここで注目すべき点は、整数値がクラス内初期化を持つことができるとしてもstatic const
、そのような変数のアドレスを取得することは許可されないということです。クラス外定義がある場合に限り、静的メンバーのアドレスを取得できます。これにより、上記の推論がさらに検証されます。
列挙型は、int が期待される場所で列挙型の値を使用できるため、これが許可されます。上記の引用を参照してください。
C++11 ではこれがどのように変わるのでしょうか?
C++11 では制限がある程度緩和されます。
C++11 9.4.2 静的データメンバー
§3
静的データメンバーがconstリテラル型の場合、クラス定義内の宣言で中括弧またはイコール初期化子すべての初期化句それは代入式は定数式です。リテラル型の静的データメンバーはクラス定義で宣言できます。その場合
constexpr specifier;
、その宣言は中括弧またはイコール初期化子すべての初期化句それは代入式は定数式です。[注: どちらの場合も、メンバーは定数式に出現する可能性があります。—注記終了] メンバーは、プログラム内で使用される場合、名前空間スコープ内で定義される必要があり、名前空間スコープ定義には初期化子が含まれてはなりません。
また、C++11意思非静的データ メンバーを、そのクラス内で宣言されている場所で初期化できるようにします (§12.6.2.8)。これにより、ユーザー セマンティクスが大幅に容易になります。
これらの機能は最新の gcc 4.7 にはまだ実装されていないため、コンパイル エラーが発生する可能性があることに注意してください。