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);
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage