C ++: Funktion wird nicht überschrieben, wenn Vorlagen verwendet werden

8

Ich bin gerade dabei, eine kleine Kalenderbibliothek für einen Schulauftrag zu schreiben, und ich bin auf ein unerwartetes und sehr verwirrendes Problem gestoßen. Mein Zuweisungsoperator wird nicht überschrieben, wenn ich Vorlagen einführe!

Also ist die Struktur die folgende: Ich habe eine abstrakte Klasse Date mit meist reinen virtuellen Funktionen (einschließlich des Zuweisungsoperators), und dann habe ich zwei Unterklassen Gregorian und Julian , die alle ihre Funktionen implementieren. Schließlich habe ich eine Vorlage für eine Klasse Calendar geschrieben, die das heutige Datum (in Form eines Gregorian oder Julian Objekts) und einige andere Dinge enthält, die für dieses spezielle Problem nicht wirklich relevant sind.

Das Problem ist, dass wenn ich versuche, dieses Mitglied today zu setzen, bekomme ich einen langen Linker-Fehler:

  

Fehler 4 Fehler LNK2019: nicht aufgelöstes externes Symbol "public: virtuelle Klasse lab2 :: Datum & amp; __thiscall lab2 :: Datum :: operator = (Klasse lab2 :: Datum const & amp;)" (?? 4Date @ lab2 @@ UAEAAV01 @ ABV01 @@ Z) referenziert in der Funktion "public: Klasse lab2 :: Gregorian & amp; __thiscall lab2 :: Gregorian :: operator = (Klasse lab2 :: Gregorian const & amp;)" (?? 4Gregorian @ lab2 @@ QAEAAV01 @ ABV01 @@ Z) C: \ Benutzer ... \ test.obj Kalender

sagt mir, dass es die Funktion operator= in der Klasse Date nicht finden kann (offensichtlich, weil es rein virtuell ist). Warum benutzt es keine der überschriebenen? Es sagt mir, dass Gregorian::operator= versucht, Date::operator= aufzurufen?

Hier ist der vereinfachte Code, bei dem Dinge schief gehen:

%Vor%

Und hier ist ein Ausschnitt von Gregorian.cpp :

%Vor%

Der (Default-) Konstruktor von Date setzt einfach die Werte von m_year , m_month und m_day auf das heutige Datum:

%Vor%

Es ist jedoch erwähnenswert, dass dies vollkommen in Ordnung ist:

%Vor%

So wird die Klasse Calendar instanziiert:

%Vor%

Gibt es hier einen sehr offensichtlichen Fehler, den ich mache?

    
hagward 01.11.2012, 19:54
quelle

4 Antworten

5

Wenn Sie die Fehlermeldung sorgfältig lesen, werden Sie zwei Dinge bemerken, der Compiler versucht, eine Definition für zu finden:

%Vor%

Und das Symbol wird benötigt aus der Definition von:

%Vor%

* Wie ist dieser Operator überhaupt in Ihrem Programm erschienen? *

Kopierkonstruktoren und Zuweisungsoperatoren sind speziell und werden immer im Programm deklariert. Entweder geben Sie eine Deklaration oder der Compiler wird es für Sie tun. Sie haben Gregorian& Gregorian::operator=(const Date&) bereitgestellt, aber Gregorian& Gregorian::operator=(const Gregorian&) wird nicht aus dem Programm entfernt.

Wenn Sie versuchen, ein Gregorian -Objekt einem anderen zuzuweisen, findet der Compiler die beiden Überladungen von Ihnen und die implizit definierte und die Überladungsauflösung wird feststellen, dass die implizit deklarierte Zuweisung eine bessere Übereinstimmung ist. Dies wird die Definition des Zuweisungsoperators ähnlich wie folgt auslösen:

%Vor%

Es gibt verschiedene Dinge, die Sie tun können, um dieses Problem zu lösen. Am einfachsten definieren Sie Date Date::operator=(const Date&) in Ihrem Programm (lassen Sie es als reine virtuelle Funktion, geben Sie aber auch eine Definition an). Auf diese Weise kann der Compiler, wenn er zwei Objekte des gleichen abgeleiteten Typs trifft, seine Magie wirken und alles wird funktionieren. Sie können dies auch verwenden, um die Implementierung der Kopie der Mitglieder der Basis zu faktorisieren, indem Sie den Abgang im abgeleiteten Typ erzwingen.

Eine weitere Option, die mehr Aufwand erfordert, besteht darin, den Zuweisungsoperator für alle abgeleiteten Typen zu deklarieren und zu definieren. Es gibt mehr Code, der hier geschrieben werden muss, und der Code, der die Mitglieder von Date behandelt, muss dupliziert werden. Wenn Sie die Basisklasse ändern, müssen Sie alle abgeleiteten Zuweisungsoperatoren aktualisieren ... Ich würde nicht gehen durch diesen Pfad.

Schließlich, und sobald der Compilerfehler behoben ist, überlegen Sie, ob Ihre Implementierung des Zuweisungsoperators für eine allgemeine Date sinnvoll ist oder nicht (was möglicherweise ein Gregorian Datum ist). Überlegen Sie, was passiert, wenn Sie Folgendes tun:

%Vor%

Beachten Sie, dass das Problem in diesem Beispiel verschwindet, wenn Sie die Definition von Date::operator= angeben und der Compiler Gregorian::operator=(Gregorian const&) für Sie generieren lässt.

    
David Rodríguez - dribeas 01.11.2012, 21:42
quelle
2

Zuallererst: Während Sie in Ihrer Klasse Date& Date::operator=(const Date&) die Klasse Date haben, generiert der Compiler die implizite Funktion Gregorian& Gregorian::operator=(const Gregorian&) für Ihre Klasse Gregorian .

Die vom Compiler generierte Funktion verhält sich wie folgt:

%Vor%

Laut Überladungsauflösungsregeln ist diese Funktion besser macth im Fall

%Vor%

dann Date& Date::operator=(const Date&)

Wenn Sie delete diese Funktion verwenden oder privat machen, wird sie immer noch deklariert und wäre besser für den Compiler geeignet, da der Compiler Zugriffsprobleme ignoriert, wenn er die Überladung wählt:

  

[ Hinweis: Die durch die Überladungsauflösung ausgewählte Funktion ist nicht garantiert   um für den Kontext angemessen zu sein. Andere Einschränkungen, wie die   Zugriff auf die Funktion, kann im aufrufenden Kontext verwendet werden   schlecht geformt. -Endnote ]

( 13.3 Überladungsauflösung, C ++ 11 Standardentwurf )

Sie sollten wahrscheinlich Realisation für Date operator= function schreiben. Sie sollten wahrscheinlich das gleiche Unterstreichungsdatenformat für alle Kalenderrealisierungen haben, dann brauchen Sie keinen virtuellen Operator =. Nun versuchen Sie eine Konvertierung durchzuführen, aber um es richtig zu machen, müssen Sie nicht nur den Typ von links operator= -Operand, sondern auch den Typ von rechts -Operanden kennen.

    
Lol4t0 01.11.2012 21:43
quelle
1

Der vereinfachte Code, der Ihr Problem veranschaulicht:

%Vor%

Ссылка

%Vor%

Sie denken, Sie nennen das

%Vor%

aber eigentlich heißt das, automatisch generierter Standardoperator:

%Vor%

Seine Standardimplementierung ruft diesen Operator auf:

%Vor%

was nicht implementiert ist - also der Fehler.

Die einfachste Lösung besteht darin, eine explizite Umsetzungszuweisung vorzunehmen:

%Vor%

Ich würde vorschlagen - A Operator nicht als virtuell zu definieren - es ergibt hier keinen Sinn.

Wenn Sie es wirklich virtuell wollen, dann implementieren Sie auch seine Basis rein virtuelle Version:

%Vor%

Oder - lösche den Standard operator = von allen, die von Klasse A abgeleitet sind - was sehr unpraktisch ist ...

    
PiotrNycz 01.11.2012 21:49
quelle
0
  

(offensichtlich, weil es rein virtuell ist).

Bist du sicher? Wie ist es definiert genau ? Ich denke, dass du es falsch definierst. Es sollte aussehen wie

%Vor%

Beachten Sie den Rückgabewert und const Date& .

    
nothrow 01.11.2012 19:58
quelle

Tags und Links