2012-12-01 3 views
62

C++ 11以前では、整数型または列挙型の静的constメンバーに対して、クラス内初期化しか実行できませんでした。 Stroustrup discusses this in his C++ FAQ、次の例を与える:だから、なぜこれらの不便な制限は存在しないC++ 11では、非静的メンバーおよび非constメンバーのクラス内初期化が可能です。何が変わったの?

:?

class Y { 
    const int c3 = 7;   // error: not static 
    static int c4 = 7;   // error: not const 
    static const float c5 = 7; // error: not integral 
}; 

そして、次の推論をクラスは通常、ヘッダーファイルで宣言され、ヘッダーファイルは通常多くの翻訳単位に含まれます。しかし、複雑なリンカー規則を避けるために、C++ではすべてのオブジェクトに一意の定義が必要です。この規則は、C++がオブジェクトとしてメモリーに格納する必要があるエンティティーのクラス定義を許可した場合には破られます。

しかし、C++ 11は、非静的メンバ(§12.6.2/ 8)の中のクラス初期化できるように、これらの制限を緩和する:場合、非委任コンストラクタにおいて

を与えられた非静的データメンバーまたは基本クラスは、mem-initializer-idmem-initializer-listがない場合を含み、コンストラクタがctor-initializerを持たないことを含む)およびエンティティ抽象クラス(10.4)の仮想基本クラスではない場合、

  • エンティティがブレースまたは同等イニシャライザを持つ非静的データメンバーである場合、エンティティは8.5で指定されたとおりに初期化されます。
  • エンティティがバリアントメンバ(9.5)である場合、初期化は実行されません。
  • エンティティは、デフォルトで初期化されます(8.5)。

彼らはconstexpr指定子でマークされている場合は、セクション9.4.2にも非const静的メンバーの中に、クラスの初期化を可能にします。

C++ 03での制限の理由はどうでしたか? 「複雑なリンカルール」を単に受け入れるか、これを実装しやすくするために変更されたものがありますか?

+5

何事もなかっ上FAQを所有しています。コンパイラはこれらすべてのヘッダー専用のテンプレートでよりスマートに成長しているので、比較的簡単に拡張できます。 –

答えて

50

コンパイラを以前よりも複雑にするという犠牲を払ってリンカを同じように保っていたという簡単な答えがあります。

これはリンカーが整列するための複数の定義をもたらす代わりに、1つの定義しか得られず、コンパイラーはそれを並べ替える必要があります。

プログラマーも同様にソートされていますが、それは大したことではないほどに単純です。あなたは、単一のメンバーのために指定された2つの異なる初期化子がある場合、余分なルールが来る:今すぐ

class X { 
    int a = 1234; 
public: 
    X() = default; 
    X(int z) : a(z) {} 
}; 

を、どのような値でこの時点の契約に余分なルールは、デフォルト以外のコンストラクタを使用する場合aを初期化するために使用されます。他の値を指定しないコンストラクタを使用する場合、1234aを初期化するために使用されますが、他の値を指定するコンストラクタを使用すると、基本的に1234が返されます無視される。例えば

#include <iostream> 

class X { 
    int a = 1234; 
public: 
    X() = default; 
    X(int z) : a(z) {} 

    friend std::ostream &operator<<(std::ostream &os, X const &x) { 
     return os << x.a; 
    } 
}; 

int main() { 
    X x; 
    X y{5678}; 

    std::cout << x << "\n" << y; 
    return 0; 
} 

結果:

1234 
5678 
+0

このように思われる前にかなり可能でした。コンパイラを書くのが難しくなりました。それは公正な陳述ですか? – allyourcode

+3

@allyourcode:はい、いいえ。はい、コンパイラの作成がより困難になりました。しかし、いいえ、C++仕様書を書くのがかなり難しくなったからです。 –

6

私はテンプレートが確定される前に、その推論が書き込まれている可能性がありますね。スタティックメンバーのクラス初期化子に必要な "複雑なリンカールール"は、C++ 11がテンプレートの静的メンバーをサポートするために既に必要でした。これは翻訳単位で、それを初期化する必要がsの定義とコードを放出する必要があります。

は、3つのすべてのケースで同じであるコンパイラのため

struct A { static int s = ::ComputeSomething(); }; // NOTE: This isn't even allowed, 
                // thanks @Kapil for pointing that out 

// vs. 

template <class T> 
struct B { static int s; } 

template <class T> 
int B<T>::s = ::ComputeSomething(); 

// or 

template <class T> 
void Foo() 
{ 
    static int s = ::ComputeSomething(); 
    s++; 
    std::cout << s << "\n"; 
} 

問題を考えてみましょうか?簡単な解決法は、どこにでもそれを放出して、リンカーがそれを並べ替えることです。だから、リンカは既に__declspec(selectany)のようなものをサポートしています。それがなければC++ 03を実装することはできませんでした。リンカを拡張する必要がないのはこのためです。

もっとぼんやりと言えば:私は古い標準で与えられている推論は単なる間違っていると思う。


UPDATE

カピルが指摘したように、私の最初の例であっても、現在の標準(C++ 14)で許可されていません。 IMOは実装(コンパイラ、リンカー)のための最も難しいケースなので、とにかくそれを残しました。私のポイントは:でも、のケースはすでに許可されているものよりも困難ではない。テンプレート使用時。

+0

これは、C++ 11の多くの機能がコンパイラに必要な機能や最適化が既に含まれている点で類似しているので、これはアップフォートを取得していませんでした。 –

+0

@AlexCourt私はこの回答を最近書きました。質問とジェリーの答えは2012年からです。だから、私の答えはあまり注目されなかったのだと思います。 –

+0

これは、静的なconstのみがクラス – Kapil

3

理論ではSo why do these inconvenient restrictions exist?...の理由が有効ですが、簡単にバイパスすることができます。これはC++ 11とまったく同じです。

にファイルが含まれていると、ファイルが含まれているだけで、初期化は無視されます。メンバは、がクラスをインスタンス化したときにのみ初期化されます。

他の言葉で言えば、初期化はまだコンストラクタと結びついていますが、表記が異なり、より便利です。コンストラクターが呼び出されない場合、値は初期化されません。

コンストラクターが呼び出された場合、値はクラス内の初期化で初期化されるか、またはコンストラクターが独自の初期化でその値をオーバーライドできます。初期化のパスは基本的にコンストラクタを介して同じです。

これはStroustrup氏から明らかであるがC++ 11

関連する問題