从派生可变模板类调用基本模板的虚方法 [英] Calling virtual method of base template from derived variadic template class
问题描述
这本质上是先前的问题的后续追踪(不是由我提出,但我对某个问题是: 为什么编译器/链接器无法解析对虚函数的调用在这种情况下,派生类是具有可变参数的模板类,它对同一模板类多次应用多重继承(对可变参数中的每个类型应用一次)。 / p> 在下面的具体示例中,派生类是 这些链接失败如 ideone 所示: A $ c> p> Work< JOB> :: work(),即 派生类可能实现/覆盖了该方法并不重要。 ( 当你删除然后显式 第一步导致歧义:多个基类包含名称 通常这可以通过将基类方法名称导入类 可变参数模板复杂化因为我不认为 有了这个结构,有问题的 This is essentially a follow-up to an earlier question (not posed by me, but I am interested in an answer). The question is: Why does the compiler/linker fail to resolve the call to the virtual function from the derived class? In this case, the derived class is a template class with variadic parameters that applies multiple inheritance against the same template class multiple times (once for each type in the variadic parameters). In the concrete example below, the derived class is These are the link failures as shown by ideone: Follow this link for demonstration of the workaround working. A
You explicitly call It doesn't matter that derived classes might have implemented/overridden that method. The ( When you remove then explicit Unfortunately the first step results in ambiguity: more than one base class contains the name Usually this can be solved by bringing the base class method names into the derived class with The variadic template complicates things since I don't think With this construct, the problematic 这篇关于从派生可变模板类调用基本模板的虚方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
JobPlant
,它正在从 Worker
。调用抽象 work()
方法无法链接,而调用 workaround()
链接并以预期的方式执行。
/home/g6xLmI/ccpFAanK.o:在main函数中:
prog.cpp :( .text.startup + 0x8e) :未定义的引用`Work< JobA> :: work(JobA const&)'
prog.cpp :( .text.startup + 0xc9):未定义引用`Work< JobB& )'
prog.cpp :( .text.startup + 0xff):未定义对`Work< JobC> :: work(JobC const&)'的引用
collect2:error:ld returned 1 exit status
作业
是一个抽象基类,它具有相关的派生类。 工作
是执行作业的抽象模板类。 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
if(dynamic_cast< const JOB *>(& job)){
p-& Work< JOB> :: work(dynamic_cast< const JOB&>(job));
// p-> Work< JOB> :: workaround(job);
return true;
}
return false;
}
};
JobPlant
是一个模板类, JOBS
找到 Worker
执行作业
。 JobPlant
从 JOBS
中的每个作业类型继承 Work
。 MyJobPlant
是 JobPlant
的实例,并实现虚拟工作
Work
抽象类中的方法。
... JOBS>
struct JobPlant:Work< JOBS> ... {
typedef Worker< JobPlant,JOBS ...>工人;
bool worker(const Job& job){return WORKER()(this,job); }
};
struct MyJobPlant:JobPlant< JobA,JobB,JobC> {
void work(const JobA&){std :: cout< 作业A. << std :: endl; }
void work(const JobB&){std :: cout<< 作业B. << std :: endl; }
void work(const JobC&){std :: cout< 作业C. << std :: endl; }
};
int main(){
JobB j;
MyJobPlant()。Worker(j);
}
Work< JOB>
中的纯虚方法。这个方法没有实现(它毕竟是纯的)。
Work< JOB> ::
表示您想要该类的版本,而不是派生类的版本。不会发生动态分派。
Work< JOB> :: work()
从派生类中的覆盖方法调用基类方法,你真的想要基类方法。)
工作< JOB> ::
,结果是一个歧义错误。当尝试解析名称 work
时,编译器首先尝试确定哪个基类包含该名称。之后,下一步将是该类中不同工作
方法中的重载解析。
work
。编译器然后不会尝试找出匹配的重载。 (它们不是真的重载,而是彼此冲突,因为它们来自不同的类)。
使用
(或者技术上称为使用
)。如果你为基类的所有工作
方法添加使用
声明,编译器将找到<$ c $
使用工作< JOBS> ::工作...;
是合法的(我的编译器不这么认为)。但是如果你手工编写基类,所有的工作方法都可以进入最终类:
template< typename JOB,typename ... JOBS>
struct CollectWork:Work< JOB> ;, CollectWork< JOBS ...> {
使用Work< JOB> :: work;
使用CollectWork< JOBS ...> :: work;
};
template< typename JOB>
struct CollectWork< JOB> :Work< JOB> {
使用Work< JOB> :: work;
};
template< typename ... JOBS>
struct JobPlant:CollectWork< JOBS ...> {
使用CollectWork< JOBS ...> :: work;
typedef Worker< JobPlant,JOBS ...>工人;
bool worker(const Job& job){return WORKER()(this,job); }
};
p-> work(dynamic_cast < JOB&>(job));
已成功编译并运行。JobPlant
, and it is being called from Worker
. Invoking the abstract work()
method fails to link, while invoking workaround()
links and executes in the expected way./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
Job
is an abstract base class, and it has associated derived classes. Work
is an abstract template class that performs a job. Worker
is a template that identifies the JOB
and performs it (struct
instead of class
purely to reduce syntax clutter):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;
}
};
JobPlant
is a template class parameterized by JOBS
, that finds a Worker
to perform a job
. The JobPlant
inherits from Work
for each job type in JOBS
. MyJobPlant
is an instance of JobPlant
, and implements the virtual work
methods from the associated Work
abstract classes.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);
}
p->Work<JOB>::work()
, that is, the pure virtual method in Work<JOB>
. This method is not implemented (it's pure after all).Work<JOB>::
says that you want the version from that class, not something from a derived class. No dynamic dispatch happens.Work<JOB>::work()
is the syntax you would use to call a base class method from a overriding method in a derived class, and there you really want the base class method.)
Work<JOB>::
, the result is an ambiguity error. When trying to resolve the name work
, the compiler first tries to determine which of the base classes contains that name. After that, the next step then would be overload resolution amongst the different work
methods in that class.work
. The compiler then never tries to figure out the matching overload. (They are not really overloads but conflicting with each other, since they are from different classes).using
(or however it's technically called what using
does). If you add using
declarations for all the work
methods of the base classes, the compiler finds the name work
in the derived class (no ambiguity) and can then proceed with overload resolution (also not ambigious).using Work<JOBS>::work...;
is legal (and my compiler doesn't think so either). But if you compose the base classes "manually", all the work methods can be brought into the final class: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); }
};
p->work(dynamic_cast<const JOB &>(job));
compiles and runs successfully.