Warum keine Vererbung von operator=
-
Hi,
nicht zu vergessen, dass der (Standard-)operator=() der Kindklasse eine andere Signatur hat als der der Elternklasse:
struct A { A& operator=(A const&); }; struct B : public A { B& operator=(B const&); };... damit würde der A::operator=() sowieso nur überladen (man hätte also 2: denmit Parameter "A const&" und den mit Parameter "B const&") und nicht "geerbt".
Gruß,
Simon2.
-
Wenn du noch erklärst, was du hier unter Erben verstehst, kann man mit dem Beitrag vielleicht etwas anfangen.
-
Hat vllt. jmd. Links, in denen beschrieben wird, wie die Veerbung funktioniert? Ich habe schon das von volkard gelesen, aber ich glaube dmait nicht klarzukommen.
-
cpp-tutor/Abgeleitete Klassen
Kannst ja mal gucken, ob du damit klar kommst.Gruß
Don06
-
Danke, ich werde mich damit morgen beschäftigen

Ich melde mich notfalls.
-
camper schrieb:
Wenn du noch erklärst, was du hier unter Erben verstehst, kann man mit dem Beitrag vielleicht etwas anfangen.
Entstammt der Ausgangsfrage:
wolfgke schrieb:
...Warum wird operator= nicht vererbt, ...?...
Gruß,
Simon2.
-
Simon2 schrieb:
Hi,
nicht zu vergessen, dass der (Standard-)operator=() der Kindklasse eine andere Signatur hat als der der Elternklasse:
struct A { A& operator=(A const&); }; struct B : public A { B& operator=(B const&); };... damit würde der A::operator=() sowieso nur überladen (man hätte also 2: denmit Parameter "A const&" und den mit Parameter "B const&") und nicht "geerbt".
Gruß,
Simon2.
Geerbte Funktionen kann man nicht überladen. Die neue Funktion namens "operator =" (die Signatur spielt hier keine Rolle bloss der Name) verdeckt die alte ("geerbte") Funktion namens "operator =".
class base { public: void arrr() {} base& operator =(base const& b) { return *this; } }; class derived : public base { public: void arrr(int) {}; derived& operator =(derived const& d) { return *this; } }; int main() { derived d; // d.arrr(); // fehler, arrr() ist von arrr(int) verdeckt d.arrr(1); // OK // d = base(); // fehler, operator =(base const& b) wird von operator =(derived const& d) verdeckt d = derived(); // OK // explizit d.base::arrr(); // OK d.base::operator = (base()); // OK }Ob in "base" oder "derived" hier ein "operator =" vom User definiert wird oder nicht ist dabei vollkommen egal, das Verhalten bezüglich der Zuweisung "d = base();" ändert sich dadurch nicht.
-
hustbaer schrieb:
...
Geerbte Funktionen kann man nicht überladen...Weiß ich, deswegen:
Simon2 schrieb:
...würde ... hätte...
Wollte nur auf die unterschiedliche Signatur hinweisen, die auch dann den gewünschten Effekt verhinderte (Konjunktiv), wenn nicht gelten würde, was camper bereits schrieb:
camper schrieb:
...In jedem Falle führt es dazu, dass alle geerbten Zuweisungsoperatoren verdeckt werden ...
... und gleichzeitig hast Du mit o.g. Aussage (absolut gesehen) auch noch Unrecht, weil:
camper schrieb:
...Mithin kann diese Verdeckung auch mit den üblichen Mitteln umgangen werden: durch Verwendung einer qualifizierten ID beim Aufruf oder einer using-Deklaration in der Klasse.

Bleiben wir einfach dabei: Es gibt 2 voneinander unabhängige Mechanismen, die die gewünschte "Vererbung" unmöglich machen:
- Verdeckung und
- falsche Signatur.Gruß,
Simon2.
-
Simon2 schrieb:
... und gleichzeitig hast Du mit o.g. Aussage (absolut gesehen) auch noch Unrecht, weil:
camper schrieb:
...Mithin kann diese Verdeckung auch mit den üblichen Mitteln umgangen werden: durch Verwendung einer qualifizierten ID beim Aufruf oder einer using-Deklaration in der Klasse.

Eine Erklärung anstelle nichtssagender Smileys würde deiner Argumentation allerdings mehr Gewicht verleihen.
Der Standard ist nicht besonders klar hinsichtlich des Begriffs Vererbung:ISO/IEC 14882:2003 schrieb:
Unless redefined in the derived class, members of a base class are also considered to be members of the derived class. The base class members are said to be inherited by the derived class. Inherited members can be referred to in expressions in the same manner as other members of the derived class, unless their names are hidden or ambiguous (10.2).
1. Nicht-statische Datenmember werden nur deklariert und nicht definiert. Statische Datenmember müssen der ODR genügen - genau eine Definition im Programm. Gleiches gilt für Funktionen, es sei denn sie sind inline. Typen dürfen nur einmal pro ÜE definiert werden. Der Begriff Redefinition ist hier also deplaziert. Zumal es ja auf die Wiederverwendung von Namen von Membern ankommt, nicht die der Member selbst - das sagt der Standard aber gerade nicht.
2. Der 3. Satz indiziert, dass Member, wenn sie verdeckt werden, nicht aus diesem Grunde auch nicht vererbt werden.
Und das war auch mein Ausgangspunkt. Andererseits widerspricht das dem Gebrauch von Vererbung in anderen Teilen des Standards, der sinngemäß so zu formulieren wäre:
Ein (benanntes) x ist Member einer Klasse O genau dann, wenn der Ausdruck o.x (o sein ein Ausdruck vom Typ O) auf dieses x verweist, und das ist im Falle der Verdeckung oder bei Mehrdeutigkeit gerade nicht der Fall.
Die Möglichkeit einer using-Deklaration, um einen Namen sichtbar zu machen, spricht auch nicht für die eine oder andere Interpretation, schließlich kann man auch ganz normal geerbte Member per using redeklarieren - natürlich unter der üblichen Restriktion, dass ein Member nur einmal in einer Klassendefinition deklariert werden darf.Es bleibt festzuhalten, dass der Begriff des vererbten Members im Standard selbst mehrdeutig ist.
Simon2 schrieb:
Bleiben wir einfach dabei: Es gibt 2 voneinander unabhängige Mechanismen, die die gewünschte "Vererbung" unmöglich machen:
- Verdeckung und
- falsche Signatur.Was die Signatur hier zu suchen hat, bleibt schleierhaft. Selbst ein Überschreiben einer virtuellen Funktion führt dazu, dass alle Funktionen (einschließlich der Überschriebenen) der Basisklassen verdeckt werden. Richtig wäre in diesem Sinne:
- Verdeckung
- MehrdeutigkeitSo oder so schweift das etwas vom eigentlichen Thema ab. Wir sind uns ja darin einig, warum ein Zuweisungsoperator einer Basisklasse nicht als Zuweisungsoperator der abgeleiteten Klasse nutzbar ist.
-
camper schrieb:
...Was die Signatur hier zu suchen hat, bleibt schleierhaft....
Hmmmm, einmal versuche ich's noch.
Was üblicherweise von einer "geerbten Funktion" erwartet, ist folgendes:struct A { void f(int); }; struct B: public A { void g(string); }; int main() { A a; a.f(3); // (*) B b; b.f(3); // geht und macht dasselbe wie (*) ...Sprich: "Wenn die Basisklasse das kann, kann es auch die Kindklasse"
So wird (meine Vermutung) auch hier die Erwartung gewesen sein: "Wenn bereits die Basisklasse kopierbar ist, dann auch die Kindklassen".
Im Gegensatz zu obigem Beispiel gibt es aber IMMER einen operator=(), der
- den der Basisklasse verdeckt UND
- eine andere Signatur hat (im Gegensatz zu f()).Aber wie Du schon gesagt hast: Ist alles OT und ich bin immer noch nicht davon überzeugt, dass ich Quatsch geschrieben habe.
Gruß,
Simon2.
-
Der Knackpunkt ist das Verdecken. Um es mit deinem Beispiel zu sagen: dein struct B hat in dem Fall immer ein void f(float) (oder string oder sogar int), entweder explizit angegeben oder implizit vom Compiler hinzugefuegt. Die Deklaration einer Funktion gleichen Namens verdeckt unabhaengig von der Signatur die Fanktion der Basisklasse, weshalb die unterschiedliche Signatur irrelevant ist.
struct A { A& operator= (const A&); }; struct B : A { A& operator= (const A&); //Aeusserst ungewoehnlich, aber machbar };Trotz der gleichen Signatur ueberdeckt hier op= den geerbten op=. Und es ist egal, ob das Ding nu op= oder f() oder schlagmichtot() heisst. Der einzige unterschied zu "normalen" Methoden ist der, dass op= vom Compiler eine implizite Deklaration verpasst bekommt fuer die Klassen, die ihn nicht explizit deklarieren, in welcher Form auch immer.
-
struct Bar; struct Foo { Bar& operator=(const Bar&); // gleiche Signatur wie Copy-Zuweisungsoperator in Bar Foo& operator=(const Foo&); }; struct Bar { using Foo::operator=; };Bar hat jetzt die Zuweisungsoperatoren
Bar& Bar::operator=(const Bar&) (implizit deklariert und verdeckt immer noch den operator gleicher Signatur in Foo)
Foo& Bar::operator=(const Foo&) (aus Foo, eigentlich verdeckt, durch using sichtbar gemacht)Die Verdeckung des Operators gleicher Signatur aus der Basisklasse ist hier allerdings eine Spezialregel der using-Deklaration - die allgemeine Regel würde Mehrdeutigkeit implizieren (dann hätte die Klasse aber gar keinen Kopierzuweisungsoperator - das darf nicht sein) oder die Verhinderung der impliziten Deklaration.
Simon2s Standpunkt ist durchaus haltbar - ich bestreite nur, das meine Argumentation widersprüchlich ist.
-
oehm Moment. Wer erbt hier von wem? In diesem beispiel gibts garkeine Vererbung und daher wird auch kein geerbter op= verdeckt. So wie es hier steht hast du zwei unabhaengige Klassen die beide die zwei gleichen Zuweisungsopis haben - wobei ich bezweifle dass das das so kompiliert, zumindest nicht, wenn du irgendwelche Member hast die du in den op= bearbeitest.
-
camper schrieb:
...ich bestreite nur, das meine Argumentation widersprüchlich ist.

Ach sooooo - das war wohl nur ein Mißverständnis: Ich meinte nicht, dass DEINE Argumentation widersprüchlich sei, sondern hustbaers Aussage "geerbte Funktionen kann man nicht überladen" (die er natürlich auch nicht in der Allgemeinheit meinte) relativieren wollen !
Dafür habe ich Deine Aussage benutzt.Sorry, dass ich das in dem Zusammenhang nicht klar genug rausgestellt habe.
Gruß,
Simon2.
-
pumuckl schrieb:
oehm Moment. Wer erbt hier von wem? ...
Naja, das ist doch wohl eher ein schnell identifizierter Typo. Gemeint ist bestimmt:
struct Bar; struct Foo { // ... }; struct Bar : Foo { // ..Würde ich jetzt jedenfalls aus dem Zusammenhang (und der Namensgebung) schließen.
pumuckl schrieb:
...
struct A { A& operator= (const A&); }; struct B : A { A& operator= (const A&); //Aeusserst ungewoehnlich, aber machbar };...
Trotzdem bekommt (IIRC) B hier noch einen operator=(const B&) verpasst, dessen Signatur "nicht passt".
Aber nun hängt Euch doch nicht so an meiner Randbemerkung auf !!

Ich habe NIEEEMALS behauptet, dass die Verdeckung unwichtig, sondern nur, dass es selbst dann nicht funktionieren würde, wenn die C++-Designer das mit der Verdeckung anders geregelt hätten.
Den fachlichen Grund dafür hat Jaxom bereits in seinem ersten Posting genannt ("...es würden nur Basisklassenanteile kopiert, es würden Redundanzen entstehen usw...."); worin sich das technisch/semantisch ausdrückt, wollte ich mit dem Hinweis auf die Signatur unterstreichen - mehr nicht.Gruß,
Simon2.
-
Simon2 schrieb:
pumuckl schrieb:
oehm Moment. Wer erbt hier von wem? ...
Naja, das ist doch wohl eher ein schnell identifizierter Typo. Gemeint ist bestimmt:
struct Bar; struct Foo { // ... }; struct Bar : Foo { // ..Jo, das wars. danke.
-
camper schrieb:
...Jo, das wars. danke.
Gern geschehen !

-
@Simon2:
Ich verstehe immer noch nicht wie du geerbte Funktionen überladen willst.
Überladen heisst für mich dass beim Aufruf auch "overload resolution" gemacht wird, und wenn ich durch den vermeintlichen "Overload" alle gleichnamigen Funktionen der Basisklasse verdecke, dann passiert eben keine "overload resolution" mehr wenn ich unqualifiziert aufrufe. Und wenn ich mit der Basisklasse qualifiziere ... dann wird der neue "Overload" in der abgeleiteten Klasse auch nicht verwendet.Also. Wie überlade ich geerbte Funktionen?
-
hustbaer schrieb:
Also. Wie überlade ich geerbte Funktionen?
Indem ich sie per using in den Scope der abgeleiteten Klasse ziehe und dann überlade

-