Rein virtuelle Methode mit Kindparameter überschreiben?



  • Hallo zusammen,

    folgender Code lässt sich nicht kompilieren:

    #include <iostream>
    
    struct A {
    	virtual int id() const = 0;
    };
    
    struct B {
    	int id() const { return 0; }
    };
    
    struct C {
    	virtual void foo(A const &a) = 0;
    };
    
    struct D : C {
    	void foo(B const &b) { std::cout << b.id(); } // Soll als Überschreiber für foo(A const &) dienen
    };
    
    int main() {
    	B b;
    	D d; // Fehler: Instanz von abstrakter Klasse kann nicht erstellt werden
    	d.foo(b);
    }
    

    Ich würde gerne eine rein virtuelle Methode (aus struct C), welche eine abstrakte Klasse (struct A) als Parameter annimmt, in der Implementierung (struct D) mit einer Implementierung dieser Klasse (struct 😎 überschreiben. Leider geht das nicht mit der oben genannten Fehlermeldung.

    Meine Frage jetzt: Gibt es da irgendeine Möglichkeit das doch so zu realisieren? Oder Alternativen?



  • Nein, es geht nicht. Ein Parameter vom Typ A ist etwas anderes als ein Parameter vom Typ B.
    Aus deinem Code vermute ich, dass du in irgendeiner Form eine virtuelle Memberfunktion haben willst, die alle Klassen annimmt, die eine Memberfunktion id() anbieten, oder?



  • Nathan schrieb:

    Nein, es geht nicht. Ein Parameter vom Typ A ist etwas anderes als ein Parameter vom Typ B.

    Mist, das hatte ich befürchtet 😃

    Nathan schrieb:

    Aus deinem Code vermute ich, dass du in irgendeiner Form eine virtuelle Memberfunktion haben willst, die alle Klassen annimmt, die eine Memberfunktion id() anbieten, oder?

    Also was ich eigentlich will: Ich habe das Visitor Pattern implementiert um "bequem" über einen Baum mit verschiedenartigen Knoten iterieren und an diesen Operationen durchführen zu können. Das funktioniert auch soweit:

    #include <memory>
    #include <vector>
    #include <iostream>
    
    struct visitor;
    
    struct acceptor {
        virtual void accept(visitor &) = 0;
    };
    
    struct node : acceptor {};
    
    struct concrete_node_1 : node {
        void accept(visitor &);
    };
    
    struct concrete_node_2 : node {
        void accept(visitor &);
    };
    
    struct visitor {
        void visit(concrete_node_1 &cn1) { std::cout << "cn1\n"; }
        void visit(concrete_node_2 &cn2) { std::cout << "cn2\n"; }
    };
    
    void concrete_node_1::accept(visitor &v) { v.visit(*this); }
    void concrete_node_2::accept(visitor &v) { v.visit(*this); }
    
    int main() {
        std::vector<std::shared_ptr<node>> nodes;
        nodes.push_back(std::make_shared<concrete_node_1>()); // 1
        nodes.push_back(std::make_shared<concrete_node_1>()); // 1
        nodes.push_back(std::make_shared<concrete_node_2>()); // 2
    
        visitor v;
    
        for (auto const &n : nodes) {
            n->accept(v); // cn1 cn1 cn2
        }
    }
    

    Allerdings würde ich jetzt gerne mehrere, verschiedene visitor implementieren, die auch unterschiedliche Dinge tun. Problem ist dann ja dass ich mit jedem neuen visitor eine neue accept Methode in den concrete_node_n Klassen implementieren muss. Daher würde ich gerne einen allgemeinen visitor übergeben, und die konkreten visitoren dann von diesem ableiten, so dass ich die concrete_node_n Klassen nicht mehr antasten muss.

    Ist das möglich?



  • Der Nachteil vom Visitor Pattern ist ja, dass sich die Klassen nicht ändern sollten. Dann kann man nämlich eine allgemeine Basisklasse für alle Visitoren dieser Hierachie machen.

    #include <memory>
    #include <vector>
    #include <iostream>
    
    struct concrete_node_1;
    struct concrete_node_2;
    
    struct visitor
    {
    	virtual void visit(concrete_node_1 &) = 0;
    	virtual void visit(concrete_node_2 &) = 0;
    };
    
    struct acceptor
    {
        virtual void accept(visitor &) = 0;
    };
    
    struct node : acceptor {};
    
    struct concrete_node_1 : node
    {
        void accept(visitor &v)
        {
        	v.visit(*this);
        }
    };
    
    struct concrete_node_2 : node
    {
        void accept(visitor &v)
        {
        	v.visit(*this);
        }
    };
    
    struct concrete_visitor_1 : visitor
    {
        void visit(concrete_node_1 &cn1) { std::cout << "v1 cn1\n"; }
        void visit(concrete_node_2 &cn2) { std::cout << "v1 cn2\n"; }
    };
    
    struct concrete_visitor_2 : visitor
    {
        void visit(concrete_node_1 &cn1) { std::cout << "v2 cn1\n"; }
        void visit(concrete_node_2 &cn2) { std::cout << "v2 cn2\n"; }
    };
    
    int main()
    {
        std::vector<std::shared_ptr<node>> nodes;
        nodes.push_back(std::make_shared<concrete_node_1>()); // 1
        nodes.push_back(std::make_shared<concrete_node_1>()); // 1
        nodes.push_back(std::make_shared<concrete_node_2>()); // 2
    
        concrete_visitor_1 v1;
        for (auto const &n : nodes)
            n->accept(v1);
        concrete_visitor_2 v2;
        for (const auto &n : nodes)
        	n->accept(v2);
    }
    

    Edit: Man kann das übrigens auch schön generalisieren, wie Alexandrescu schon in Modern C++ Design gezeigt hat: http://ideone.com/zZd7Ru



  • Ahhh, jetzt hats Klick gemacht!

    Ja, so passt das ganz gut zu dem was ich will, vielen Dank 👍


Log in to reply