it-swarm.com.de

Undefinierter Verweis auf statisches Klassenmitglied

Kann jemand erklären, warum der folgende Code nicht kompiliert werden kann? Zumindest unter g ++ 4.2.4.

Und was noch interessanter ist, warum wird es kompiliert, wenn ich MEMBER auf int setze?

#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

Sie müssen das statische Element tatsächlich irgendwo definieren (nach der Klassendefinition). Versuche dies:

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

const int Foo::MEMBER;

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

Das sollte die undefinierte Referenz loswerden.

191
Drew Hall

Das Problem ergibt sich aus einem interessanten Zusammenprall der neuen C++ - Funktionen und dem, was Sie versuchen. Schauen wir uns zuerst den Push_back Unterschrift:

void Push_back(const T&)

Es wird ein Verweis auf ein Objekt vom Typ T erwartet. Nach dem alten Initialisierungssystem existiert ein solches Mitglied. Der folgende Code kann beispielsweise problemlos kompiliert werden:

#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;
}

Dies liegt daran, dass es irgendwo ein tatsächliches Objekt gibt, in dem dieser Wert gespeichert ist. Wenn Sie jedoch zur neuen Methode zum Angeben statischer const-Member wechseln, wie oben beschrieben, wird Foo::MEMBER ist kein Objekt mehr. Es ist eine Konstante, ähnlich wie:

#define MEMBER 1

Aber ohne die Kopfschmerzen eines Präprozessor-Makros (und mit Typensicherheit). Das bedeutet, dass der Vektor, der eine Referenz erwartet, keine bekommen kann.

71
Douglas Mayle

Der C++ - Standard erfordert eine Definition für Ihr statisches const-Mitglied, wenn die Definition irgendwie benötigt wird.

Die Definition ist erforderlich, wenn beispielsweise die Adresse verwendet wird. Push_back verwendet seinen Parameter als Konstantenreferenz. Der Compiler benötigt daher unbedingt die Adresse Ihres Members und muss diese im Namespace definieren.

Wenn Sie die Konstante explizit umwandeln, erstellen Sie eine temporäre Konstante, die an die Referenz gebunden ist (gemäß den Sonderregeln in der Norm).

Dies ist ein wirklich interessanter Fall, und ich denke, es lohnt sich, ein Problem anzusprechen, damit der Standard geändert wird, um dasselbe Verhalten für Ihr konstantes Mitglied zu haben!

In seltsamer Weise könnte dies jedoch als legitime Verwendung des unären '+' - Operators angesehen werden. Grundsätzlich ist das Ergebnis der unary + ist ein rvalue und daher gelten die Regeln für die Bindung von rvalues ​​an const-Referenzen und wir verwenden nicht die Adresse unseres statischen const-Members:

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

Keine Ahnung, warum die Besetzung funktioniert, aber Foo :: MEMBER wird erst beim ersten Laden von Foo zugewiesen, und da Sie es nie laden, wird es nie zugewiesen. Wenn Sie irgendwo einen Hinweis auf ein Foo hätten, würde es wahrscheinlich funktionieren.

1
Paul Tomblin

Zur zweiten Frage: Push_ref verwendet eine Referenz als Parameter, und Sie können keine Referenz auf statische Komponenten einer Klasse/Struktur haben. Sobald Sie static_cast aufrufen, wird eine temporäre Variable erstellt. Und ein Verweis auf dieses Objekt kann übergeben werden, alles funktioniert einwandfrei.

Oder zumindest mein Kollege, der das gelöst hat.

0
Quarra

Mit C++ 11 wäre dies für Basistypen wie möglich

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

Der constexpr -Teil erstellt ein statisches Ausdruck im Gegensatz zu einem statischen Variable - und das verhält sich genauso wie eine extrem einfache Inline-Methodendefinition. Der Ansatz erwies sich jedoch mit C-String-Konstanten innerhalb von Template-Klassen als etwas wackelig.

0
starturtle