Неоднозначный вызов, если класс наследует от 2 шаблонных родительских классов. Зачем? - PullRequest
3 голосов
/ 24 августа 2010

У меня есть шаблонный класс, который выполняет действие с классом, заданным в качестве аргумента шаблона. Для некоторых из моих классов я хочу «сгруппировать» функциональность в один класс, чтобы сделать его проще для вызывающего. На самом деле код выглядит примерно так (имена были изменены):

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

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

Проблема в том, что когда я вызываю ProcessingFrontEnd :: process с CustomerOrder в качестве аргумента, компилятор жалуется на это.

Я попытался воспроизвести проблему в меньшем тестовом приложении. Это код:

#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?

EDIT: Если я явно добавлю 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 ]

3 голосов
/ 24 августа 2010

Это по замыслу.Компилятор не пытается разрешить перегруженные функции, потому что это не перегруженные функции.Стандарт действительно ясно об этом (см. 10.2.2).Если одно и то же имя встречается в двух разных базах, это неоднозначно, даже если их можно правильно разрешить с помощью вызова (т. Е. В вашем случае).Функции с одинаковыми именами в разных классах обычно имеют совершенно разные цели, и поэтому выбор между ними не следует делать на основе их аргументов.Есть много веских причин, чтобы не допустить этого, но вот одна.

Представьте, что ваш класс C является производным от A и B, и эти два базовых класса происходят из двух разных библиотек.Если автор B добавляет новую функцию в класс, он может нарушить код пользователя, перенаправив вызов из A :: foo () в B :: foo (), если последний лучше соответствует.

Если вы хотите, чтобы две функции обрабатывались так же, как если бы они были частью одного класса, то лучший способ сделать это - использовать объявления в производном классе.Просто добавьте

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

к объявлению класса X.

2 голосов
/ 24 августа 2010

Я полагаю, что вы нарушаете правила перегрузки C ++, которые запрещают перегрузку между классами . Вы получили бы те же результаты, если бы ваши классы шаблонов были двумя отдельными классами, каждый со своим собственным членом process(CustomerOrder) и process(ProductionOrder).

Обходной путь - это явные using операторы внутри вашего производного класса, извлекающие каждую перегрузку из каждого базового класса шаблона.

0 голосов
/ 24 августа 2010

Как компилятор должен знать, какой процесс вы хотите вызвать?Есть два варианта.Вы хотите и то, и другое, или одно?

Вам необходимо переопределить процесс в производном классе.

...