Überladen von []/=



  • Hi.

    Ich bin im Moment dabei sowas ähnliches wie eine map<> nachzubauen und habe ein kleines Problem mit dem Operator []. Mit folgendem Code habe ich ihn überladen:

    t& operator[](const k key)
    {
        return content[key];
    }
    

    Funktioniert sowohl beim Auslesen als auch beim Ändern eines Wertes in der Variable "content" (bei der es sich um eine normale map<k,t> handelt). Problem ist aber, dass ich beim Zuweisen (map[k] = t) nicht nur den Wert ändern möchte, sondern auch noch etwas an einer anderen Variable der Klasse ändern möchte (vector<k> um genau zu sein). Wenn ich aber noch den =-Operator überlade wird das überhaupt nicht beachtet, die Methode wird nicht einmal aufgerufen (nur bei map = k). Wie mache ich es also richtig?



  • So einfach ist das nicht. Wenn Du eine Referenz zurück lieferst, kann der Aufrufer damit machen, was er will. Ein Lösung wäre ein Proxy-Objekt zurück zu liefern, der den Zuweisungsoperator überlädt. Das Proxy-Objekt muß dann eine Referenz auf Deine Map halten, um diese dann über die Zuweisung zu benachrichtigen.

    template <typename t>
    class MyMap;
    
    template <typename T>
    class MyProxy
    {
        MyMap& myMap;
        T& myValue;
      public:
        MyProxy(myMap& m, T& v)
          : myMap(m), myValue(v)
        { }
        // mach Proxy-objekt konvertierbar in den ursprünglichen Typ
        operator T&             { return myValue; }
        operator const T& const { return myValue; }
        MyProxy& operator= (const T& newVal)
        {
          myValue = newVal;
          myMap.notify(newVal);
        }
    };
    
    template <typename t>
    class MyMap
    {
        friend class MyProxy<t>;
        void notify(const t&)
        {
          // es wurde eine Zuweisung durchgeführt
        }
      public:
        MyProxy<t> operator[](const k key) 
        { 
           return MyProxy<t>(*this, content[key]); 
        }
    };
    

    Gruß

    Tommi



  • So einfach ist das nicht.

    Das hatte ich befürchtet 🙄
    Nunja, ich gucke mir deine Idee mal an und melde mich dann bei Erfolg/Misserfolg.



  • So, ich habe jetzt mal versucht deinen Code zu kompilieren (ohne operator[] in MyMap erstmal) aber ich bekomme einige Fehler. Als Anfänger würde ich es begrüßen eine kompilierbare Version von deiner Idee zu bekommen 🙂

    7 [...] ISO C++ forbids declaration of `MyMap' with no type
    14 [...] invalid member function declaration 
    15 [...] `const' qualifiers cannot be applied to `const T&'
    


  • Natürlich muß die siebte Zeile lauten "MyMap<T>& myMap;" - da die Map-Klasse ja ein Template ist. Und bei den Konvertierungsoperatoren fehlen die Klammern "operator T&()" bzw. "operator const T&() const"



  • OK, einen haben wir noch:

    10 [...] expected `)' before '&' token
    

    Ein <T> muss da jedenfalls nicht hin.



  • "MyProxy(MyMap<T>& m, T& v)"

    *nochmal den Code durchsieht* mir fallen jetzt keine Fehler mehr auf


  • Mod

    selbstverständlich muss da <T> hin und ausserdem muss es großgeschrieben werden, denn wir meinen ja das template...



  • Ok, jetzt gehts. Mit <T> hatte es wegen der falschen Groß/Kleinschreibung von MyMap nicht funktioniert.



  • So, ich habe die Proxy-Klasse jetzt eingebaut und alles funktioniert wie gewünscht. Danke!


  • Mod

    operator T&             { return myValue; }
        operator const T& const { return myValue; }
    

    keine gute idee - das führt nähmlich schnell zu mehrdeutigkeiten, ausserdem könnte man den proxy so (scheinbar) umgehen. denn sowohl ein T& als auch ein const T& können gleichermassen benutzt werden um ein T zu erzeugen. die auswahl von benutzerdefinierten konvertierungsoperatoren richtet sich NICHT nach den const-qualifierern des arguments sondern nur nach den argumenten der zielfunktion - alle konvertierungs-operatoren (soweit aufrufbar) sind gleichermassen gut.

    besser:

    operator T const { return myValue; }
    

    und dafür ein const overload für op[] in MyMap das einfach ein T zurückgibt. sobald man mit proxies arbeitet sollte man nirgend mehr referenzen verwenden (höchtens als optimierung). denn ein

    &map[..]
    

    ist dann nicht mehr sinnvoll.



  • camper schrieb:

    und dafür ein const overload für op[] in MyMap das einfach ein T zurückgibt.

    Wie genau sollte es dann aussehen?


  • Mod

    t operator[](const k key) const
    {
        return content[key];
    }
    


  • operator T const { return myValue; }
    

    Das geht so nicht wie ich grade feststelle.

    105 [...] invalid member function declaration
    


  • camper schrieb:

    operator T&             { return myValue; }
        operator const T& const { return myValue; }
    

    keine gute idee - das führt nähmlich schnell zu mehrdeutigkeiten, ausserdem könnte man den proxy so (scheinbar) umgehen. denn sowohl ein T& als auch ein const T& können gleichermassen benutzt werden um ein T zu erzeugen. die auswahl von benutzerdefinierten konvertierungsoperatoren richtet sich NICHT nach den const-qualifierern des arguments sondern nur nach den argumenten der zielfunktion - alle konvertierungs-operatoren (soweit aufrufbar) sind gleichermassen gut.

    Ne, die Lage ist klar.richtig ist, dass beide rückgabewerte gleich gut sind, aber es ist nicht egal, welche der beiden überladungen aufgerufen wird. das const in der 2. methode hat ne große wirkung ;).

    (flugs nochmal getestet)

    struct test{
        int i,j;
        test():i(1),j(2){}
        operator int&(){//klammern net vergessen
            return i;
        }
    
        operator const int&()const{//dito
            return j;
        }
    };
    int main(){
        test t;
        cout<<t<<endl;
    	cout<<const_cast<const test&>(t);
    	int i;
    	cin>>i;
    }
    

    ausgabe: 1 2


  • Mod

    hm ja, stimmt - hatte mal ein problem mit solchen overloads, aber das war doch noch ein bisschen anders. ich bleibe aber dabei, dass es keine gute idee ist, referenzen auf interna eines proxies herauszugeben, da das dem zweck des proxies doch irgendwie zuwiderläuft. und mit konvertierungs-operatoren sollte man sowieso sehr vorsichtig umgehen.



  • du musst ja nicht interna des proxys zurückgeben, sondern interna des objekts, vor das sich der proxy vorschaltet ;).



  • Nachdem ich jetzt etwas weitergemacht habe, gibt es aber doch noch ein Problem. Beispielsweise geht folgendes nicht:

    umap<string, umap<string,string> > test;
    test["foo"]["bar"] = "test";
    

    Auch das nicht:

    struct t {
    	string foo;
    	string bar;
    };
    
    umap<string,t> test2;
    test2["foo"].foo = "test";
    

    Diese Funktionalität ist mir aber von äußerster Wichtigkeit. Also was tun?



  • und _was_ geht nicht?



  • Alles bis auf das Deklarieren geht nicht.

    30 [...] 'class MyProxy<std::string, t>' has no member named 'foo'
    

Anmelden zum Antworten