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


  • Mod

    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


  • Mod

    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 😕


  • Mod

    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 😉


Anmelden zum Antworten