Undefinierter Verweis auf statisches Klassenmitglied

Lesezeit: 6 Minuten

Undefinierter Verweis auf statisches Klassenmitglied
Pawel Piatkowski

Kann jemand erklären, warum der folgende Code nicht kompiliert wird? Zumindest auf g++ 4.2.4.

Und interessanter, warum wird es kompiliert, wenn ich MEMBER in int umwandle?

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

  • Ich habe die Frage bearbeitet, um den Code um vier Leerzeichen einzurücken, anstatt

     

    zu verwenden. Das bedeutet, dass die spitzen Klammern nicht als HTML interpretiert werden.

    – Steve Jessop

    7. November 2008 um 19:03 Uhr

  • stackoverflow.com/questions/16284629/… Sie können sich auf diese Frage beziehen.

    – Aqeel Raza

    3. Oktober 2018 um 0:18 Uhr

  • Seit C ++ 17 ist keine zusätzliche Definition erforderlich, siehe meine Antwort unten.

    – Quimby

    10. März 2021 um 17:23 Uhr

Undefinierter Verweis auf statisches Klassenmitglied
Zeichnete Hall

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

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

const int Foo::MEMBER;

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

Das sollte die undefinierte Referenz loswerden.

  • Guter Punkt, die Inline-Static-Const-Integer-Initialisierung erstellt eine Scoped Integer-Konstante, deren Adresse Sie nicht übernehmen können, und Vector nimmt einen Referenzparameter.

    – Evan Teran

    7. November 2008 um 18:08 Uhr

  • Diese Antwort behandelt nur den ersten Teil der Frage. Der zweite Teil ist viel interessanter: Warum funktioniert das Hinzufügen eines NOP-Casts, ohne dass die externe Deklaration erforderlich ist?

    – Brent Bradburn

    1. Februar 2011 um 0:48 Uhr


  • Ich habe gerade viel Zeit damit verbracht, herauszufinden, dass, wenn sich die Klassendefinition in einer Header-Datei befindet, die Zuordnung der statischen Variablen in der Implementierungsdatei und nicht im Header erfolgen sollte.

    – Shanet

    14. Juli 2012 um 3:06 Uhr

  • @shanet: Sehr guter Punkt – das hätte ich in meiner Antwort erwähnen sollen!

    – Zeichnete Hall

    14. Juli 2012 um 3:10 Uhr

  • Aber wenn ich es als const deklariere, kann ich dann nicht den Wert dieser Variablen ändern?

    – Namratha

    27. November 2012 um 4:57 Uhr

Undefinierter Verweis auf statisches Klassenmitglied
Douglas Mayle

Das Problem entsteht durch einen interessanten Konflikt zwischen neuen C++-Features und dem, was Sie zu tun versuchen. Werfen wir zunächst einen Blick auf die push_back Unterschrift:

void push_back(const T&)

Es erwartet einen Verweis auf ein Objekt des Typs T. Unter dem alten Initialisierungssystem existiert ein solches Element. Der folgende Code lässt sich beispielsweise problemlos kompilieren:

#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 zu der neuen Methode zum Angeben statischer konstanter Elemente wechseln, wie Sie es oben getan haben, Foo::MEMBER ist kein Objekt mehr. Es ist eine Konstante, etwas ähnlich wie:

#define MEMBER 1

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

  • danke, das hat geholfen … das könnte sich für stackoverflow.com/questions/1995113/strangest-language-feature qualifizieren, wenn es nicht schon da ist …

    – André Holzner

    3. Dezember 2010 um 13:45 Uhr

  • Erwähnenswert ist auch, dass MSVC die Non-Cast-Version ohne Beanstandungen akzeptiert.

    – Porgen

    26. Juni 2012 um 23:49 Uhr

  • -1: Das ist einfach nicht wahr. Sie sollten immer noch statische Member definieren, die inline initialisiert werden, wenn dies der Fall ist odr-gebraucht irgendwo. Dass Compiler-Optimierungen Ihren Linker-Fehler beseitigen können, ändert daran nichts. In diesem Fall ist Ihre lvalue-zu-rvalue-Konvertierung (dank der (int) Cast) tritt in der Übersetzungseinheit mit perfekter Sichtbarkeit der Konstante auf, und die Foo::MEMBER ist nicht mehr odr-gebraucht. Dies steht im Gegensatz zum ersten Funktionsaufruf, bei dem eine Referenz herumgereicht und an anderer Stelle ausgewertet wird.

    – Leichtigkeitsrennen im Orbit

    6. Oktober 2014 um 22:12 Uhr


  • Wie wäre es mit void push_back( const T& value );? const&‘s können mit rvalues ​​verbunden werden.

    – Kostas

    24. Februar 2020 um 21:38 Uhr

1647283212 859 Undefinierter Verweis auf statisches Klassenmitglied
Richard Korden

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

Die Definition ist erforderlich, wenn beispielsweise dessen Adresse verwendet wird. push_back nimmt seinen Parameter als const-Referenz, und so benötigt der Compiler unbedingt die Adresse Ihres Members und Sie müssen sie im Namespace definieren.

Wenn Sie die Konstante explizit umwandeln, erstellen Sie eine temporäre und diese temporäre, die an die Referenz gebunden ist (unter besonderen Regeln im Standard).

Dies ist ein wirklich interessanter Fall, und ich denke tatsächlich, dass es sich lohnt, ein Problem anzusprechen, damit die std geändert wird, um das gleiche Verhalten für Ihr ständiges Mitglied zu haben!

Obwohl dies auf seltsame Weise als legitime Verwendung des unären ‘+’-Operators angesehen werden könnte. Im Grunde 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-Mitglieds:

v.push_back( +Foo::MEMBER );

  • +1. Ja, es ist sicherlich seltsam, dass für ein Objekt x vom Typ T der Ausdruck “(T) x” verwendet werden kann, um eine konstante Referenz zu binden, während das einfache “x” dies nicht kann. Ich liebe deine Beobachtung zu “unary +”! Wer hätte gedacht, dass das arme kleine “unary +” tatsächlich einen Nutzen hat… 🙂

    – j_random_hacker

    29. Mai 2009 um 10:38 Uhr

  • Denken Sie an den allgemeinen Fall … Gibt es einen anderen Objekttyp in C++, der die Eigenschaft hat, dass er (1) nur als Lvalue verwendet werden kann, wenn er definiert wurde, aber (2) in einen Rvalue konvertiert werden kann, ohne dass dies der Fall ist definiert?

    – j_random_hacker

    29. Mai 2009 um 10:51 Uhr

  • Gute Frage, und zumindest fallen mir im Moment keine anderen Beispiele ein. Dies ist wahrscheinlich nur hier der Fall, weil das Komitee hauptsächlich nur die vorhandene Syntax wiederverwendete.

    – Richard Corden

    29. Mai 2009 um 12:47 Uhr

  • @RichardCorden: Wie hat das unäre + das gelöst?

    – BLUTGEFAHR

    28. Juni 2018 um 15:10 Uhr

  • @Blood-HaZaRd: Bevor rvalue auf die einzige Überladung von verweist push_back war ein const &. Die direkte Verwendung des Mitglieds führte dazu, dass das Mitglied an die Referenz gebunden war, was eine Adresse voraussetzte. Jedoch Hinzufügen der + erstellt ein temporäres mit dem Wert des Members. Die Referenz bindet sich dann an dieses temporäre, anstatt zu verlangen, dass das Mitglied eine Adresse hat.

    – Richard Corden

    13. Juli 2018 um 10:48 Uhr

1647283212 247 Undefinierter Verweis auf statisches Klassenmitglied
iso9660

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;

In C++17 gibt es eine einfachere Lösung mit inline Variablen:

struct Foo{
    inline static int member;
};

Dies ist eine Definition von member, nicht nur seine Erklärung. Ähnlich wie bei Inline-Funktionen verstoßen mehrere identische Definitionen in verschiedenen Übersetzungseinheiten nicht gegen ODR. Es ist nicht mehr erforderlich, eine bevorzugte .cpp-Datei für die Definition auszuwählen.

1647283212 567 Undefinierter Verweis auf statisches Klassenmitglied
Marc

Nur noch ein paar Zusatzinfos:

C++ erlaubt es, konstante statische Typen von Ganzzahl- und Aufzählungstypen als Klassenmitglieder zu “definieren”. Aber das ist eigentlich keine Definition, nur ein “Initialisierungs-Marker”

Sie sollten trotzdem außerhalb des Unterrichts eine Definition Ihres Mitglieds schreiben.

9.4.2/4 – Wenn ein statisches Datenelement vom Typ Konstante Ganzzahl oder Konstante Aufzählung ist, kann seine Deklaration in der Klassendefinition einen Konstanten-Initialisierer spezifizieren, der ein ganzzahliger konstanter Ausdruck sein soll (5.19). In diesem Fall kann das Mitglied in ganzzahligen Konstantenausdrücken erscheinen. Das Mitglied muss weiterhin in einem Namespace-Bereich definiert werden, wenn es im Programm verwendet wird, und die Definition des Namespace-Bereichs darf keinen Initialisierer enthalten.

1647283213 871 Undefinierter Verweis auf statisches Klassenmitglied
Paul Tomblin

Keine Ahnung, warum die Besetzung funktioniert, aber Foo::MEMBER wird erst zugewiesen, wenn Foo zum ersten Mal geladen wird, und da Sie es nie laden, wird es nie zugewiesen. Wenn Sie irgendwo einen Verweis auf ein Foo hätten, würde es wahrscheinlich funktionieren.

  • Ich denke, Sie beantworten Ihre eigene Frage: Die Besetzung funktioniert, weil sie (eine vorübergehende) Referenz erstellt.

    – Jaap Versteegh

    30. November 2011 um 15:43 Uhr

1002760cookie-checkUndefinierter Verweis auf statisches Klassenmitglied

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy