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.