Aufrufen der virtuellen Methode der Basisvorlage aus der abgeleiteten variadischen Vorlagenklasse
Dies ist im Wesentlichen ein Follow-up zueine frühere Frage (nicht von mir gestellt, aber ich bin an einer Antwort interessiert).
Die Frage ist Warum löst der Compiler / Linker den Aufruf der virtuellen Funktion aus der abgeleiteten Klasse nicht auf? In diesem Fall ist die abgeleitete Klasse eine Schablonenklasse mit variadischen Parametern, die mehrere Vererbungen für dieselbe Schablonenklasse mehrmals anwendet (einmal für jeden Typ in den variadischen Parametern).
Im konkreten Beispiel unten lautet die abgeleitete KlasseJobPlant
, und es wird von @ aufgerufWorker
. Aufruf des Abstractswork()
-Methode kann keine Verknüpfung herstellen, während @ aufgerufen wiworkaround()
verknüpft und wird wie erwartet ausgeführt.
Dies sind die Verbindungsfehler, wie durch @ gezei ideone:
/home/g6xLmI/ccpFAanK.o: In function `main':
prog.cpp:(.text.startup+0x8e): undefined reference to `Work<JobA>::work(JobA const&)'
prog.cpp:(.text.startup+0xc9): undefined reference to `Work<JobB>::work(JobB const&)'
prog.cpp:(.text.startup+0xff): undefined reference to `Work<JobC>::work(JobC const&)'
collect2: error: ld returned 1 exit status
Folge diesem Lin zur Demonstration der Problemumgehung.
Job
ist eine abstrakte Basisklasse und ihr sind abgeleitete Klassen zugeordnet.Work
ist eine abstrakte Vorlagenklasse, die einen Job ausführt.Worker
ist eine Vorlage, die das @ identifizieJOB
und führt es aus struct
Anstatt vonclass
nur um die Syntax-Unordnung zu reduzieren):
struct Job { virtual ~Job() {} };
struct JobA : Job {};
struct JobB : Job {};
struct JobC : Job {};
template <typename JOB>
struct Work {
virtual ~Work() {}
virtual void work(const JOB &) = 0;
void workaround(const Job &job) { work(dynamic_cast<const JOB &>(job)); }
};
template <typename PLANT, typename... JOBS> struct Worker;
template <typename PLANT, typename JOB, typename... JOBS>
struct Worker<PLANT, JOB, JOBS...> {
bool operator()(PLANT *p, const Job &job) const {
if (Worker<PLANT, JOB>()(p, job)) return true;
return Worker<PLANT, JOBS...>()(p, job);
}
};
template <typename PLANT, typename JOB>
struct Worker<PLANT, JOB> {
bool operator()(PLANT *p, const Job &job) const {
if (dynamic_cast<const JOB *>(&job)) {
p->Work<JOB>::work(dynamic_cast<const JOB &>(job));
//p->Work<JOB>::workaround(job);
return true;
}
return false;
}
};
A JobPlant
ist eine Template-Klasse, die von @ parametrisiert wiJOBS
, das ein @ findWorker
um ein @ auszuführjob
. DasJobPlant
erbt vonWork
für jeden Jobtyp inJOBS
. MyJobPlant
ist eine Instanz vonJobPlant
und implementiert das virtuellework
Methoden aus dem zugehörigenWork
abstrakte Klassen.
template <typename... JOBS>
struct JobPlant : Work<JOBS>... {
typedef Worker<JobPlant, JOBS...> WORKER;
bool worker(const Job &job) { return WORKER()(this, job); }
};
struct MyJobPlant : JobPlant<JobA, JobB, JobC> {
void work(const JobA &) { std::cout << "Job A." << std::endl; }
void work(const JobB &) { std::cout << "Job B." << std::endl; }
void work(const JobC &) { std::cout << "Job C." << std::endl; }
};
int main() {
JobB j;
MyJobPlant().worker(j);
}