Verzwickte Operatorenüberladung
-
Hallo zusammen,
nachdem ich Google nun mehrere Stunden mit Suchanfragen erfolglos vergewaltigt habe, versuche ich es nun hier...
Weiß zufällig jemand, ob es möglich ist arithmetische Operatoren (+,-,*,/) derart zu überladen, daß das Objekt der jeweiligen Klasse als rechtsseitiger Operand fungiert, ohne den Operator global zu definieren?Kleines Beispiel:
class class1 { private: int i; public: void operator = (int v) {this->i=v;} int operator - (int v) {return this->i-v;} //<- normale Vorgehensweise }; class1 bla; bla=5; int i=bla-2; //Funktioniert, da Objekt linksseitig int a=10-bla; //Das ist es, was ich gerne hätte, aber nicht funktioniert
Kann mir vielleicht jemand weiterhelfen?
Vielen Dank schonmal
-
Das geht nur, wenn du den Operator global definierst.
-
Mist...ich hatte gehofft, daß es vielleicht doch irgendeinen Kniff gibt, den ich nicht gefunden habe
Dank dir recht herzlich
-
Der Operator muss nicht global sein, es genügt eine Überladung im die Klasse umschließenden Namensraum damit der Operator per ADNL gefunden werden kann.
-
So hatte ich es schon probiert, und so klappts ja theoretisch auch.
Mein Problem ist, daß ich mit duplicate definition Linker Fehlern bombardiert werde, wenn ich meine Operatoren auf namespace Ebene definiere...und das bekomme ich einfach nicht gebacken.Deshalb war ich eben auf der Suche nach ein anderen Lösung.
Aber hilft ja nix...ich muß wohl mein Klassendesign überarbeiten
-
Hydrael schrieb:
Mein Problem ist, daß ich mit duplicate definition Linker Fehlern bombardiert werde, wenn ich meine Operatoren auf namespace Ebene definiere...
Und warum versuchst du nicht herauszufinden, warum es zu diesem Fehler kommt bzw. wie dieser zu beheben ist?
-
camper schrieb:
Und warum versuchst du nicht herauszufinden, warum es zu diesem Fehler kommt bzw. wie dieser zu beheben ist?
Das habe ich natürlich - bin halt nur nicht dahinter gekommen.
Ich kann ja mal einen kurzen Abriss des Ganzen geben:Ich möchte gerne verschiedenste Datentypen in einer Klasse wrappen.
Hierfür habe ich folgende Basisklasse:#ifndef _GDBDATATYPE_H_ #define _GDBDATATYPE_H_ #include "CompileOptions.h" #include <stdio.h> #include <string> namespace GDB { template <class T> class GDBDataType abstract { public: //! Has to be inherited. Creates a new instance of a GDBDataType GDBDataType(void) { } //! Returns the current value T getValue(void) const {return this->m_Value;} /************************ * Operators * ************************/ virtual void operator = (T v){this->m_Value=v;} virtual void operator = (GDBDataType v){this->m_Value=v.getValue();} virtual bool operator == (T v){return this->m_Value==v;} virtual bool operator == (GDBDataType v){return this->m_Value==v.getValue();} virtual bool operator != (T v){return this->m_Value!=v;} virtual bool operator != (GDBDataType v){return this->m_Value!=v.getValue();} virtual bool operator > (T v){return this->m_Value>v;} virtual bool operator > (GDBDataType v){return this->m_Value>v.getValue();} virtual bool operator < (T v){return this->m_Value<v;} virtual bool operator < (GDBDataType v){return this->m_Value<v.getValue();} virtual bool operator >= (T v){return this->m_Value>=v;} virtual bool operator >= (GDBDataType v){return this->m_Value>=v.getValue();} virtual bool operator <= (T v){return this->m_Value<=v;} virtual bool operator <= (GDBDataType v){return this->m_Value<=v.getValue();} //------------------------------------------------------------ //! Destructor virtual ~GDBDataType(void) { } //! Returns a std::string representing the current value virtual std::string toString(void)=0; protected: //! Value this object is holding T m_Value; }; } #endif
Für integer basierende Datentypen habe ich eine weitere, von obiger Klasse abgeleitete Basisklasse:
#ifndef _GDBNUMERICDATATYPE_H_ #define _GDBNUMERICDATATYPE_H_ #include "CompileOptions.h" #include "GDBDataType.h" namespace GDB { template <class T> class GDBNumericDataType abstract : public GDBDataType<T> { public: //! Has to be inherited. Creates a new instance of a GDBDataType GDBNumericDataType(void) { } //Standard arithmetic operators (+,-,*,/) have to be declared global //within every inherited class and implemented in Operators.h virtual void operator += (T v){this->m_Value+=v;} virtual void operator += (GDBNumericDataType v) {this->m_Value+=v.getValue();} virtual void operator -= (T v){this->m_Value-=v;} virtual void operator -= (GDBNumericDataType v) {this->m_Value-=v.getValue();} virtual void operator *= (T v){this->m_Value*=v;} virtual void operator *= (GDBNumericDataType v) {this->m_Value*=v.getValue();} virtual void operator /= (T v){this->m_Value/=v;} virtual void operator /= (GDBNumericDataType v) {this->m_Value/=v.getValue();} virtual void operator ++ (){this->m_Value++;} virtual void operator -- (){this->m_Value--;} //------------------------------------------------------------ //! Destructor virtual ~GDBNumericDataType(void) { } }; } #endif
So, und jetzt gehts im Prinzip los. Nehmen wir als beispiel meinen short Wrapper:
#ifndef _GDBINT16_H_ #define _GDBINT16_H_ #include "CompileOptions.h" #include "GDBNumericDataType.h" namespace GDB { #ifdef OS_WINDOWS class DLL_EXPORT GDBInt16 : public GDBNumericDataType<short> #else class GDBInt16 : public GDBNumericDataType<short> #endif { public: //! Creates a new instance of an GDBInt16 GDBInt16(void); //------------------------------------------------------------ //! Destructor ~GDBInt16(void); //--------------------------------------------------------- //! Returns a std::string representing the current value std::string toString(void); //--------------------------------------------------------- void operator = (short v); void operator = (GDBInt16 v); }; short operator + (const GDBInt16 &v1, const GDBInt16 v2){return v1.getValue()+v2.getValue();} short operator + (const GDBInt16 &v1, const short v2){return v1.getValue()+v2;} short operator + (const short &v1,const GDBInt16 v2){return v1+v2.getValue();} short operator - (const GDBInt16 &v1, const GDBInt16 v2){return v1.getValue()-v2.getValue();} short operator - (const GDBInt16 &v1, const short v2){return v1.getValue()-v2;} short operator - (const short &v1,const GDBInt16 v2){return v1-v2.getValue();} short operator * (const GDBInt16 &v1, const GDBInt16 v2){return v1.getValue()*v2.getValue();} short operator * (const GDBInt16 &v1, const short v2){return v1.getValue()*v2;} short operator * (const short &v1,const GDBInt16 v2){return v1*v2.getValue();} short operator / (const GDBInt16 &v1, const GDBInt16 v2){return v1.getValue()/v2.getValue();} short operator / (const GDBInt16 &v1, const short v2){return v1.getValue()/v2;} short operator / (const short &v1,const GDBInt16 v2){return v1/v2.getValue();} } #endif
Bis jetzt funktioniert das Ganze natürlich noch.
Include ich meine GDBInt16.h allerdings mehrmals, krachts wegen oben erwähnten duplicate definitions.
Und warum das passiert - da bin ich ganz ehrlich - kapier ich nicht
-
Operatorüberladungen sind, abgesehen davon, dass sie keinen Namen haben, ganz normale Funktionen. Als solche unterliegen sie den gleichen Regel bzgl. der ODR. Funktionen (mit external linkage), die nicht inline sind, dürfen im gesamten Programm nur einmal definiert werden. Funktionen, die inline sind, dürfen in jeder Übersetzungseinheit einmal definiert werden (vorausgesetzt, die Definitionen sind in den unterschiedlichen ÜEs äquivalent). Funktionen, die in einer Klassendefinition definiert werden, sind inline ohne dass sie explizit als solche gekennzeichnet werden müssen. Für Funktionen, die außerhalb einer Klassendefinition definiert gilt dies nicht - du müsstest also überall noch ein inline hinzufügen oder sie in der Klasse definieren (per friend-Deklaration).
Hinsichtlich dieser Regeln besteht im übrigen überhaupt kein Unterschied zwischen Memberfunktionen und normalen Funktionen - da aber Memberfunktionen sowieso nur in der Klasse deklariert werden können (abgesehen von der einzigen möglichen Redeklaration außerhalb, die gleichzeitig Definition ist), fällt das nicht gleich auf).
-
Ah, jetzt hat es geklingelt.
Vielen Dank camper - jetzt funktionierts