lhs/rhs Unterscheidung dank Operator () Überladung?



  • Am einfachsten wäre es (da der Proxy ja nur für einen einzigen Typen eingesetzt wird) die entsprechenden Methoden als Weiterleitung in der Proxyklasse zu implementieren:

    class Bla
    {
        class TollerProxy
        {
        public:
           void abgefahrene_methode () const
           {
               BlaZeiger->getValue(bla, bla).abgefahrene_methode();
           }
    //...
        };
    //...
    };
    


  • Ok habs gefunden..

    T operator ->() const  {
       return graph->getWeight( a, b);
    }
    

    Ok den . Operator kann ich nicht überladen..
    Folgendes Codebeispiel

    Graph< pair <double,double> > g;
    g(2,4) = pair<double, double>(3, 4);
    
    g(2,4).first = 3; // "Fehler: »class Graph_UW<std::pair<double, double> >::foo<std::pair<double, double> >« hat kein Element namens »first«
    

    Irgend ne Möglichkeit das zu maskieren bzw ihn zu zwingen ZUERST zu casten und dann . zu verwenden?

    Und gerade noch ein Problem gefunden mit

    Graph< baseclass* > g(10,NULL);
    dynamic_cast<derived_class*>(g(2,4))->some_member_of_derived_class(); //"Fehler: ungültiges dynamic_cast vom Typ »Graph<baseclass*>::foo<baseclass*>« in den Typ »derivedclass*«
    

    Wenn ich das umschreibe als

    baseclas* b = g(2,4);
    dynamic_cast<derived_class*>(b)->some_member_of_derived_class();
    

    funzt es natürlich. Wenn ich das ganz am Beispiel von static_cast<double>(Graph< int >) mache nörgelt er nicht.

    Edit:
    Das passiert wenn man zu lange am Text schreibt 😉 danke..
    Und nein, der Proxy wird leider jetzt schon für 3 verschiedene Typen eingesetzt und soll so in eine Bibliothek übernommen werden. Dafür natürlich möglichst transparent. Mit den bald mal im C++ Standrad aufgenommenen template "Abfragen" könnte man vielleicht zwischen pointer und normalem Typ unterscheiden, aber derzeit ist das wirklich hässlich.



  • LudiKalell schrieb:

    Irgend ne Möglichkeit das zu maskieren bzw ihn zu zwingen ZUERST zu casten und dann . zu verwenden?

    Ich glaube das mit dem casten hast du nicht ganz verstanden.. Wenn du mit g(2,4)=... drauf zugreifst, wird nichts gecastet, es wird einfach nur der operator= aufgerufen, der die rechte Seite des Gleich-Zeichens übergeben bekommt und die setXXX-Methode des Graphen aufruft.

    LudiKalell schrieb:

    g(2,4).first = 3;
    

    Nochmal: Den Operator "Punkt" kannst du nicht überladen, die Proxy-Klasse hat keinen Member namens "first", deshalb klappt auch g(2,4).bla nicht! Wenn du den Pfeil-Operator gut überladen hast, kannst du mit g(2.4)->first += 1389; drauf zugreifen, dafür ist er ja da.



  • Ich hab meinen text vom letzten Post nur stehen lassen, siehe edit, hatte deinen Text gelesen.
    Ja dass bei dem Beispiel nur der Zuweisungsoperator ne Rolle spielt hab ich übersehn. Hatte das mit dem cast etc. schon cverstanden, ich hab überall couts gesetzt und seh welche Funktion wann aufgerufen wird für verschiedene Szenarien weil ich mir anfangs nich sicher war.

    Dennoch: Ich benutze den Graphen für Pointer genauso wie für normale Objekte. Demzufolge
    T operator->() const
    Dieser hat die erwünschte Auswirkung für Pointer Objekte. Man muss nicht wissen dass da ein Pointer dahintersteckt. Und ich benutz sehr oft Pointer..
    Die Verwendung ist also transparent. Für normale Objekte gibts dann den Compilefehler.. Nagut, ich könnte.. hrmm..
    T operator->() const
    zusätzlich überladen. Und definieren dann operator ->() eben nen T
    zurückgibt.
    Dann bei Pointer ->* benutzen und sonst statt . nen ->()

    Ne bessere Lösung möglich?

    edit: oder ich schreib 2 Varianten, eine für Pointer, eine für Objekte, und überprüfe im Constructor irgendwie dass sie entsprechend falsch verwendet werden. Was aber glaube ich nicht geht.. obwohl.. doch.
    Müsste ja im Construktor schon nen this haben und damit mal probeweise son -> operator anwenden, dann sollte der Compiler missbrauch verbieten.



  • Gut ich lege das Problem lieber bei Seite. Es scheint keine wirklich gute Lösung zu geben für den . Operator wenn man Transparenz möchte. Hab nun ne at() Funktion für den speziellen Fall.

    Aber dann bleibt immer noch folgendes evtl. lösbares Problem:

    Graph< baseclass* > g(10,NULL);
    baseclass* b;
    dynamic_cast<derived_class*>(g(2,4))->some_member_of_derived_class(); //"Fehler: ungültiges dynamic_cast vom Typ »Graph<baseclass*>::foo<baseclass*>« in den Typ »derivedclass*«
    dynamic_cast<derived_class*>(b = g(2,4))->some_member_of_derived_class(); //funzt!
    

    Das passiert auch mit C cast, static_cast, reinterpret_cast, ...



  • Den ->* Operator kannst Du auch nicht überladen, ausserdem sähe das vermutlich syntaktisch gräßlich aus 🙂

    Über Spezialisierung wäre sowas machbar:

    template<class T> // macht aus T einen zeiger für alle nicht-zeigertypen
    struct graph_pointer_helper
    {
        typedef T* type;
        static type pointer( T& value ) { return &value; }
    };
    
    template<class T> // spezialisierung für zeiger reicht nur durch
    struct graph_pointer_helper<T*>
    {
        typedef T* type;
        static type pointer( T* value ) { return value; }
    };
    
    template<class T>
    class Graph
    {
    public:
        explicit Graph( T const& value = T() ) : value( value ) {}
    
        typename graph_pointer_helper<T>::type operator->() { return graph_pointer_helper<T>::pointer( value ); }
    
    private:
        T value;
    };
    
    int main()
    {
        Graph<string> a;
        a->assign( "hallo" );
    
        Graph<string*> b( new string );
        b->assign( "hallo" );
    }
    


  • LordJaxom schrieb:

    Den ->* Operator kannst Du auch nicht überladen

    Ich denke, das ist möglich...



  • Naja ->* ist schon überladbar, sagt MSDN, das inet und auch mein Compiler 😉

    Ja der Code sieht schön aus, nur leider halt Spezialisierung, der Graph soll wie gesagt alles möglich an verschiedenen Klassenpointern nehmen(gut das ist nicht das Problem, da funzt T operator->() ja einwandfrei und transparent) und eben auch noch Objekte. Gut für die Objekte könnte man dann ne Spezialisierung einfügen, aber so wie ich das verstehe muss ich die Spezialisierung dann auch für alle Pointertypen definieren.
    Und selbst wenn nicht: es soll einfach funktionieren, für jeden Typen.

    Ich habe glaube mittlerweile eine zumindest für mich befriedigende Lösung gefunden:
    Da der Anwender eh schonmal so oder so nicht drum rumkommt die Klasse wenigstens etwas zu kennen (-> für jeglichen Memberzugriff und kein operator. erlaubt oder ähnliches) hab ich einfach folgendes festgelegt:

    const T& operator() (size_t,size_t)
    foo<T>& operator [](size_t a)
    foo<T>& foo<T>::operator [](size_t B) {b = B; return *this;}
    T& at(size_t)
    // Schreibzugriff:
    g[i][j] = x; 
    // Lesezugriff:
    x = g(i,j);
    // cast:
    x = static_cast<class*>(g(i,j));
    //Memberzugriff wenn T Pointertyp
    g[i][j]->blub(); 
    //Memberzugriff wenn T kein Pointertyp
    g(i,j).blub(); //wenn blub() const
    g.at(i,j).blub(); //sonst
    

    Ist zwar an der Grenze der Transparenz, aber da der User eh den header anschaun muss noch ok..

    Wenn sonst keine weiteren Vorschläge kommen dann nochmal
    DANKE DANKE DANKE
    an alle und vor allem an Badestrand 👍



  • LudiKalell schrieb:

    Ja der Code sieht schön aus, nur leider halt Spezialisierung, der Graph soll wie gesagt alles möglich an verschiedenen Klassenpointern nehmen(gut das ist nicht das Problem, da funzt T operator->() ja einwandfrei und transparent) und eben auch noch Objekte. Gut für die Objekte könnte man dann ne Spezialisierung einfügen, aber so wie ich das verstehe muss ich die Spezialisierung dann auch für alle Pointertypen definieren.
    Und selbst wenn nicht: es soll einfach funktionieren, für jeden Typen.

    Tut es doch, die Spezialisierung spezialisiert ja nur für Pointer. Für alle anderen Typen gilt das Basistemplate. Einschränkungen auf einen bestimmten Typ sehe ich hier nicht.



  • Spezialisierung für Pointer wäre grauenhaft. Wir verwenden alle möglichen Basispointer und Pointer auf Subklassen, und es kommt noch Kram dazu. Locker 10 Spezialisierungen wären da nötig, und der Code bläht sich auf, evtl. auch die Compilezeit? Naja ich behalt's mal im Kopf, vielleicht ist das am Ende die beste Lösung.


Anmelden zum Antworten