如果类继承自2个模板化父类,则调用不明确。为什么?

时间:2010-08-24 11:06:12

标签: c++ templates multiple-inheritance visual-c++-2010

我有一个模板化的类,它对作为模板参数给出的类执行操作。对于我的一些课程,我想在一个类中“分组”功能,以使调用者更容易。事实上,代码看起来像这样(名称已更改):

template<typename T>
class DoSomeProcessing
{
public:
   process(T &t);
};

class ProcessingFrontEnd : public DoSomeProcessing<CustomerOrder>, public DoSomeProcessing<ProductionOrder>
{
};

问题在于,当我使用CustomerOrder作为参数调用ProcessingFrontEnd :: process时,编译器会抱怨它。

我试图在较小的测试应用程序中重现该问题。这是代码:

#include <vector>

class X : public std::vector<char>
        , public std::vector<void *>
{
};

int main(void)
{
X x;
x.push_back('c');
return 0;
}

事实上,如果编译它,Microsoft的VS2010编译器会出现此错误:

test.cpp
test.cpp(11) : error C2385: ambiguous access of 'push_back'
        could be the 'push_back' in base 'std::vector<char,std::allocator<char> >'
        or could be the 'push_back' in base 'std::vector<void *,std::allocator<void *> >'
test.cpp(11) : error C3861: 'push_back': identifier not found

我使用不同类型(char + void *,double + void *)和调用中的不同参数('c',3.14)测试了此测试应用程序,但错误消息始终相同。

我用VS2005和VS2010进行了测试,但我总是得到同样的错误。

为什么编译器无法确定要调用的正确函数?是什么让编译器感到困惑?或者它只是Microsoft编译器中的一个错误?

修改 如果我向我的类显式添加2个push_back方法,如下所示:

class X : public std::vector<char>
        , public std::vector<void *>
{
public:
   void push_back(char c) {}
   void push_back(void *p) {}
};

编译器不再抱怨了。因此,通过这些方法,他可以清楚地区分字符和空指针。如果两个push_back方法是从父级继承的,为什么不能这样做呢?

3 个答案:

答案 0 :(得分:3)

这是设计的。编译器没有尝试解决重载问题 函数,因为它们重载 功能。标准非常明确 (见10.2.2)。如果在两个中找到相同的名称 不同的基础,这是一种模糊,即使他们 可以通过电话正确解决(即在 你的情况)。不同类中的同名函数通常具有完全不同的目的,因此它们之间的选择不应基于 他们的论点。有很多很好的理由不这样做 允许,但这是一个。

想象一下你的C类来自A和B 这两个基类来自两个不同的类别 库。如果B的作者添加了新功能 对于类,它可能会破坏用户的代码 将调用从A :: foo()重定向到B :: foo()if 后者是一个更好的匹配。

如果您希望以与它们相同的方式处理这两个函数 如果是单个类的一部分,那么最好的方法就是使用 派生类中的声明。只需添加

using std::vector<char>::push_back;
using std::vector<void *>::push_back;

到X类的声明。

答案 1 :(得分:2)

我相信你违反了prohibit overloading across classes的C ++重载规则。如果您的模板类是两个单独的类,每个类都有自己的process(CustomerOrder)process(ProductionOrder)成员,则会得到相同的结果。

变通方法是派生类中的显式using语句,从每个模板基类中提取每个重载。

答案 2 :(得分:-1)

编译器应该如何知道您要调用哪个进程?有两种选择。你想要两个,一个还是另一个?

您需要覆盖派生类中的进程。