static
クラスのデータメンバーを初期化できないのはなぜですか?
C ++標準では、静的定数の整数型または列挙型のみをクラス内で初期化できます。これが、a
初期化が許可されているのに他のユーザーが初期化できない理由です。
参照:
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 };
};
なぜ規格はこれを許可しないのですか?
Bjarneはこれをここで適切に説明しています:
クラスは通常、ヘッダーファイルで宣言され、ヘッダーファイルは通常、多くの翻訳単位に含まれます。ただし、複雑なリンカールールを回避するために、C ++ではすべてのオブジェクトに一意の定義が必要です。C ++がオブジェクトとしてメモリに格納する必要があるエンティティのクラス内定義を許可した場合、そのルールは破られます。
static const
クラス内初期化が許可されるのはなぜ整数型と列挙型だけなのですか?
答えはBjarneの引用に隠され
ています。「C ++ではすべてのオブジェクトに一意の定義が必要です。C++がオブジェクトとしてメモリに格納する必要のあるエンティティのクラス内定義を許可した場合、そのルールは破られます。」
static const
コンパイル時定数として扱うことができるのは整数のみであることに注意してください。コンパイラーは整数値がいつでも変更されないことを知っているため、独自のマジックを適用して最適化を適用できます。コンパイラーは、そのようなクラスメンバーを単にインライン化します。つまり、それらはもはやメモリに格納されません。 、それはBjarneによって言及されたルールの例外をそのような変数に与えます。
ここで、static const
整数値がクラス内初期化を持つことができる場合でも、そのような変数のアドレスを取得することは許可されていないことに注意してください。静的メンバーのアドレスを取得するには、クラス外の定義がある場合に限ります。これにより、上記の理由がさらに検証されます。
列挙型の値は、intが期待される場所で使用できるため、列挙型が許可されます。上記の引用を参照してください
これはC ++ 11でどのように変わりますか?
C ++ 11は制限をある程度緩和します。
C ++ 11 9.4.2静的データメンバー
§3
静的データメンバーがconstリテラル型である場合、クラス定義での宣言は、brace-or-equal-initializerを指定できます。この場合、assignment-expressionであるすべてのinitializer-clauseは定数式です。リテラルタイプの静的データメンバが持つクラス定義内で宣言することができそうである場合、その宣言は、指定しなければならないブレース-OR-等しい初期におけるすべての初期化句である代入式をconstexpr specifier;
定数式です。[注:これらのどちらの場合でも、メンバーは定数式に現れることがあります。—end note]メンバーがプログラムで使用され、名前空間スコープの定義に初期化子が含まれていない場合、メンバーは名前空間スコープで定義されます。
また、C ++ 11はだろう、それが宣言される非静的データメンバは、(そのクラスに)初期化する(§12.6.2.8)を可能にします。これは、非常に簡単なユーザーセマンティクスを意味します。
これらの機能は最新のgcc 4.7ではまだ実装されていないため、コンパイルエラーが発生する可能性があることに注意してください。