2010-08-24 2 views
3

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

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

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

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

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

#include <vector> 

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

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

И действительно, если это компилируется, VS2010 компилятор Microsoft дает эту ошибку:

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 

Я испытал это тестовое приложение с различными типами (полукокс + ничтожными *, двойной + 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) {} 
}; 

Компилятор не жалуется больше. Таким образом, с помощью этих методов он может четко различать символ и указатель void. Почему он не может это сделать, если два метода push_back наследуются от родителя?

+0

Если вы не знаете, что вы делаете, inherting из StD контейнеров является плохой идеей. –

+0

@Stephane, я знаю, но это просто для иллюстрации проблемы более простым способом. На самом деле, я наследую свой собственный шаблонный класс, а не из std-контейнеров. – Patrick

+0

Не могли бы вы показать строку, на которую жалуется компилятор? Являются ли классы «CustomerOrder» и «ProductionOrder» несвязанными? (Ваше тестовое приложение не подходит, как указывает Стефан, стандартные контейнеры полны интересных трюков, которые обычные классы не имеют или не нуждаются, и, кроме того, указатели void и целые типы, такие как символы, слишком близки для удобных разрешение перегрузки. Лучше предоставить больше информации о реальной проблеме.) –

ответ

3

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

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

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

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

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

+0

Да, все. По-видимому, это предотвращает ошибки пользователя, а не то, что компилятор не может решить. Добавление предложений использования действительно решает проблему. Благодарю. – Patrick

+0

@Patrick Это только одна из многих областей несогласованности на C++. Конструкция нормальной функции-члена настаивала на том, что перегрузка не будет работать по областям, – curiousguy

+0

«_Imagine ваш класс C происходит от A и B, и эти два базовых класса приходят из двух разных библиотек.» «Теперь представьте себе ту же историю с пространствами имен. Различные, непоследовательные правила. – curiousguy

-1

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

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

+0

Компилятор знает об этом из типов шаблонов. Первый родительский класс в примере (std :: vector ) имеет метод push_back (char), а второй родительский класс (std :: vector ) имеет метод push_back (void *). Почему это отличается от наличия двух методов push_back в моем классе? – Patrick

+1

Похож на «сокрытие имени» - но но basic.scope.hiding в стандарте C++ не относится к этому случаю. Прогулка через basic.lookup может привести к явному или неявному правилу, которое может еще не дать вам обоснования. – peterchen

+0

Пользователь, очевидно, хочет, чтобы была выбрана наилучшая соответствующая функция. Так же, как любое разрешение перегрузки. – curiousguy

2

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^