Konvertierungsfehler im virtuellen Funktionsaufruf



  • Hi!
    Ich habe einen vector von verschiedenen Arbeiter-Klassen, die sich sowohl in Template-Argumenten alsauch durch Vererbung speziell unterscheiden. Sie sollen Nachrichten verarbeiten.
    Mein Beispiel:

    #include <iostream>
    #include <vector>
    #include <memory>
    
    template <typename T>
    struct derived;
    struct special;
    
    struct trait_A {
    	typedef int type;
    };
    
    struct trait_B {
    	typedef double type;
    };
    
    struct service {
    	void handler(derived<trait_A>*, int);
    	void handler(special*, double);
    };
    
    struct base { // polymorphe Basisklasse für homogenen vector
    	virtual void dispatch(service*, int) = 0;
    	virtual ~base() { }
    };
    
    template <typename Trait>
    struct derived : base { // allgemeine Ableitung
    	typedef typename Trait::type type;
    
    	type get_value(int data) {
    		return type(data > 0 ? 10 : 11);
    	}
    
    	virtual void dispatch(service* s, int data) {
    		std::cout << "Derived dispatch" << std::endl;
    		s->handler(this, get_value(data));
    	}
    
    	virtual void derived_specific(type t) {
    		std::cout << "Derived function: " << t << std::endl;
    	}
    
    	virtual ~derived() { }
    };
    
    struct special : derived<trait_B> { // speziellere Ableitung
    	void dispatch(service* s, int data) {
    		std::cout << "Special dispatch" << std::endl;
    		s->handler(this, get_value(data));
    	}
    
    	void special_specific(type d) {
    		std::cout << "Double: " << d << std::endl;
    	}
    };
    
    void service::handler(derived<trait_A>* lhs, int rhs) {
    	lhs->derived_specific(rhs);
    }
    
    void service::handler(special* lhs, double rhs) {
    	lhs->special_specific(rhs);
    }
    
    int main() {
    	service s;
    	std::vector<base*> workers{ new derived<trait_A>, new special };
    	int data = -10;
    	for (base* ptr : workers) {
    		ptr->dispatch(&s, data *= -1);
    	}
    	std::cin.get();
    }
    

    Was ich haben will:

    base::dispatch(...) --(virtual)--> derived<trait_A>::dispatch(...) --> service::handler(derived<trait_A>*, int) --> derived<trait_A>::derived_specific(...)
    base::dispatch(...) --(virtual)--> special::dispatch(...) --> service::handler(special*, double) --> special::special_specific(...)
    

    Mit der Programmausgabe:

    Derived dispatch
    Derived function: 11
    Special dispatch
    Double: 10
    

    Stattdessen:
    Visual Studio:

    > 'void service::handler(special *,double)' : Konvertierung von Argument 1 von 'derived<trait_B> *const ' in 'derived<trait_A> *' nicht möglich
    > Die Typen, auf die verwiesen wird, sind nicht verknüpft; die Konvertierung erfordert einen reinterpret_cast-Operator oder eine Typumwandlung im C- oder Funktionsformat.
    

    GCC:

    prog.cpp: In instantiation of ‘void derived<T>::dispatch(service*, int) [with Trait = trait_B]’:
    prog.cpp:74:1:   required from here
    prog.cpp:37:3: error: invalid conversion from ‘derived<trait_B>* const’ to ‘special*’ [-fpermissive]
       s->handler(this, get_value(data));
       ^
    prog.cpp:62:6: error:   initializing argument 1 of ‘void service::handler(special*, double)’ [-fpermissive]
     void service::handler(special* lhs, double rhs) {
          ^
    

    Ich hab gerade Tomaten auf den Augen. Warum geht das schief?



  • Überlege mal, was da passiert wenn Trait==trait_A:

    virtual void derived<Trait>::dispatch(service* s, int data) {
            std::cout << "Derived dispatch" << std::endl;
            s->handler(this, get_value(data));
        }
    

    Es gibt keinen passenden Handler, weil nur welche für specialized (!= derived<trait_A>) und derived<trait_B> da sind.

    Hier der Fix:

    struct base { // polymorphe Basisklasse für homogenen vector
      virtual void dispatch(service*, int) = 0;
      virtual ~base() { }
    };
    
    template <typename Trait>
    struct abstract_derived : base { // allgemeine Ableitung
      typedef typename Trait::type type;
    
      type get_value(int data) {
        return type(data > 0 ? 10 : 11);
      }
    
      virtual void dispatch(service* s, int data)=0;
    
      virtual void derived_specific(type t) {
        std::cout << "Derived function: " << t << std::endl;
      }
    
      virtual ~abstract_derived() { }
    };
    
    template <typename Trait>
    struct derived : abstract_derived<Trait> { // allgemeine Ableitung
      virtual void dispatch(service* s, int data) {
        std::cout << "Derived dispatch" << std::endl;
        s->handler(this, this->get_value(data));
      }
    };
    
    struct special : abstract_derived<trait_B> { // speziellere Ableitung
      virtual void dispatch(service* s, int data) override {
        std::cout << "Special dispatch" << std::endl;
        s->handler(this, get_value(data));
      }
    
      void special_specific(type d) {
        std::cout << "Double: " << d << std::endl;
      }
    };
    


  • Wow danke.

    > Es gibt keinen passenden Handler, weil nur welche für specialized (!= derived<trait_A>) und derived<trait_B> da sind.

    Aber warum muss ein solcher existieren? Mein voriger Code hat doch a priori keinen Handler für derived<trait_A> aufgerufen. 😕



  • Guy Incognito schrieb:

    Aber warum muss ein solcher existieren? Mein voriger Code hat doch a priori keinen Handler für derived<trait_A> aufgerufen. 😕

    Ist halt so in C++. Du hast ihn zwar nicht aufgerufen, aber instanziiert.

    special leitet von derived<trait_B> ab, deshalb muss derived<trait_B> eine valide Klasse sein. Ist es aber nicht, weil dispatch nicht kompiliert werden kann. Deshalb der Fehler.


Anmelden zum Antworten