jxh*_*jxh 13 c++ polymorphism templates variadic-templates c++11
这基本上是对早期问题的跟进(不是我提出的,但我对答案很感兴趣).
问题是: 为什么编译器/链接器无法解析派生类对虚函数的调用?在这种情况下,派生类是具有可变参数的模板类,该参数对同一模板类多次应用多次继承(对于可变参数中的每个类型一次).
在下面的具体示例中,派生类是JobPlant,并且正在调用它Worker.调用抽象work()方法无法链接,同时调用workaround()链接并以预期方式执行.
这些是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
Run Code Online (Sandbox Code Playgroud)
请点击此链接以演示变通方法的工作原理.
Job是一个抽象基类,它具有关联的派生类.Work是一个执行作业的抽象模板类.Worker是一个标识JOB并执行它的模板(struct而不是class纯粹减少语法混乱):
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;
}
};
Run Code Online (Sandbox Code Playgroud)
A JobPlant是一个参数化的模板类JOBS,它找到一个Worker执行a 的模板类job.在JobPlant从继承Work于各种作业类型JOBS.MyJobPlant是一个实例JobPlant,并work从关联的Work抽象类实现虚方法.
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);
}
Run Code Online (Sandbox Code Playgroud)
您显式调用p->Work<JOB>::work(),即纯虚方法Work<JOB>.这个方法没有实现(毕竟它是纯粹的).
派生类可能已实现/重写该方法并不重要.该Work<JOB>::说,你想从这个类,而不是从一个派生类的东西的版本.没有动态调度.
(Work<JOB>::work()您将使用从派生类中的重写方法调用基类方法的语法,并且您确实需要基类方法.)
当你删除然后显式时Work<JOB>::,结果是一个歧义错误.尝试解析名称时work,编译器首先尝试确定哪个基类包含该名称.之后,下一步将是work该类中不同方法之间的重载决策.
不幸的是,第一步导致歧义:多个基类包含名称work.然后编译器永远不会试图找出匹配的重载.(它们不是真正的重载,而是相互冲突,因为它们来自不同的类).
通常这可以通过将基类方法名称带入派生类来解决using(或者在技术上称为什么using).如果using为work基类的所有方法添加声明,编译器会work在派生类中找到名称(没有歧义),然后可以继续进行重载解析(也不是ambigious).
可变参数模板使事情复杂化,因为我认为using Work<JOBS>::work...;不合法(我的编译器也不这么认为).但是如果你"手动"编写基类,所有的工作方法都可以进入最后的类:
template <typename JOB, typename... JOBS>
struct CollectWork : Work<JOB>, CollectWork<JOBS...> {
using Work<JOB>::work;
using CollectWork<JOBS...>::work;
};
template <typename JOB>
struct CollectWork<JOB> : Work<JOB> {
using Work<JOB>::work;
};
template<typename... JOBS>
struct JobPlant : CollectWork<JOBS...> {
using CollectWork<JOBS...>::work;
typedef Worker<JobPlant, JOBS...> WORKER;
bool worker(const Job &job) { return WORKER()(this, job); }
};
Run Code Online (Sandbox Code Playgroud)
使用此构造,有问题的p->work(dynamic_cast<const JOB &>(job)); 编译并成功运行.
| 归档时间: |
|
| 查看次数: |
1399 次 |
| 最近记录: |