web-dev-qa-db-ja.com

静的クラスメンバーへの未定義の参照

誰も次のコードがコンパイルされない理由を説明できますか?少なくともg ++ 4.2.4では。

さらに興味深いことに、MEMBERをintにキャストするとコンパイルされるのはなぜですか?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.Push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.Push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}
188

実際にどこかで(クラス定義の後に)静的メンバーを定義する必要があります。これを試して:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

それは未定義の参照を取り除く必要があります。

191
Drew Hall

問題は、C++の新しい機能とあなたがやろうとしていることの興味深い衝突が原因です。まず、Push_back署名を見てみましょう。

void Push_back(const T&)

タイプTのオブジェクトへの参照が必要です。初期化の古いシステムでは、そのようなメンバーが存在します。たとえば、次のコードは問題なくコンパイルできます。

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.Push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.Push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

これは、その値が格納されている実際のオブジェクトがどこかにあるためです。ただし、上記のように静的constメンバーを指定する新しい方法に切り替えると、Foo::MEMBERはオブジェクトではなくなります。これは定数であり、次のようなものです。

#define MEMBER 1

ただし、プリプロセッサマクロの頭痛の種はありません(タイプセーフティを使用)。つまり、参照を予期しているベクトルは参照を取得できません。

71
Douglas Mayle

定義が何らかの形で必要な場合、C++標準では静的constメンバーの定義が必要です。

たとえば、アドレスが使用されている場合、定義が必要です。 Push_backはconst参照によってパラメーターを取得するため、厳密にはコンパイラーはメンバーのアドレスを必要とし、名前空間で定義する必要があります。

明示的に定数をキャストすると、テンポラリが作成され、参照にバインドされるのはこのテンポラリです(標準の特別な規則の下で)。

これは非常に興味深いケースであり、実際に問題を提起する価値があると思うので、stdを変更して、定数メンバーに対して同じ動作をさせます。

ただし、奇妙な方法で、これは単項の「+」演算子の合法的な使用と見なすことができます。基本的に、unary +の結果は右辺値であるため、右辺値をconst参照にバインドする規則が適用され、静的constメンバーのアドレスは使用しません。

v.Push_back( +Foo::MEMBER );
58
Richard Corden

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;
10
iso9660

キャストが機能する理由はわかりませんが、Foo :: MEMBERは最初にFooがロードされるまで割り当てられません。 Fooへの参照がどこかにあれば、おそらく機能するでしょう。

1
Paul Tomblin

2番目の質問に関して:Push_refはパラメーターとして参照を取り、クラス/構造体の静的constメンバーへの参照を持つことはできません。 static_castを呼び出すと、一時変数が作成されます。そして、このオブジェクトへの参照を渡すことができ、すべてがうまく機能します。

または少なくともこれを解決した私の同僚はそう言った。

0
Quarra

C++ 11では、上記のような基本的な型が可能になります。

class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

constexpr部分は、静的変数ではなく、静的を作成します。これは、非常に単純なインラインメソッド定義のように動作します。ただし、テンプレートクラス内のC文字列constexprsを使用すると、アプローチが少し不安定になります。

0
starturtle