Как C ++ выбирает, какую перегруженную функцию вызывать? - PullRequest
4 голосов
/ 22 декабря 2008

Скажите, у меня есть три класса:

class X{};
class Y{};
class Both : public X, public Y {};

Я хочу сказать, что у меня есть два класса, а затем третий класс, который расширяет оба (множественное наследование).

Теперь скажите, что у меня есть функция, определенная в другом классе:

void doIt(X *arg) { }
void doIt(Y *arg) { }

и я вызываю эту функцию с экземпляром обоих:

doIt(new Both());

Это вызывает ошибку во время компиляции, утверждая, что вызов функции неоднозначен.

В каких случаях, кроме этого, компилятор C ++ решает, что вызов неоднозначен, и выдает ошибку, если таковая имеется? Как компилятор определяет, что это за случаи?

Ответы [ 6 ]

7 голосов
/ 22 декабря 2008

Просто: если это неоднозначно, то компилятор выдает ошибку, заставляя вас выбирать. В вашем фрагменте вы получите другую ошибку, потому что тип new Both() является указателем на Both, тогда как обе перегрузки doIt() принимают свои параметры по значению (то есть они не принимают указатели). Если вы изменили doIt() для получения аргументов типов X* и Y* соответственно, компилятор выдаст вам ошибку о неоднозначном вызове функции.

Если вы хотите явно вызвать один или другой, вы приводите аргументы соответствующим образом:

void doIt(X *arg) { }
void doIt(Y *arg) { }
Both *both = new Both;
doIt((X*)both);  // calls doIt(X*)
doIt((Y*)both);  // calls doIt(Y*)
delete both;
5 голосов
/ 22 декабря 2008

Я получаю эту ошибку с gcc:

jeremy@jeremy-desktop:~/Desktop$ g++ -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18: error: call of overloaded ‘doIt(Both&)’ is ambiguous
test.cpp:7: note: candidates are: void doIt(X)
test.cpp:11: note:                 void doIt(Y)
3 голосов
/ 27 декабря 2008

Это прекрасный пример использования boost::implicit_cast:

void doIt(X *arg) { }
void doIt(Y *arg) { }

doIt(boost::implicit_cast<X*>(new Both));

В отличие от других решений (включая static_cast), приведение не будет выполнено, если неявное преобразование из Both* в X* невозможно. Это делается с помощью хитрости, лучше всего показанной на простом примере:

X * implicit_conversion(X *b) { return b; }

Вот что такое boost::implicit_cast, просто это шаблон, который сообщает тип b.

2 голосов
/ 22 декабря 2008

Компилятор выполняет поиск по глубине, а не поиск по ширине для выбора перегрузок. Полный ответ на исключительный Херб Саттер C ++ , к сожалению, у меня нет книги в руках.

Редактировать: теперь получил книгу под рукой Это называется первое правило глубины, которое называется «Принцип интерфейса» :

Принцип интерфейса для класса X, все функции, в том числе бесплатные функции, которые оба (а) "упомянуть" X и (b) "поставляются с" X логически являются частью X, потому что они образуют часть интерфейса X.

но есть вторичное правило, называемое "Поиск Кенига" , которое усложняет задачу.

Цитата: "(упрощенно): если вы поставите Аргумент функции типа класса (здесь х, типа A :: X), чтобы найти правильное имя функции компилятор считает совпадающие имена в пространство имен (здесь А), содержащее тип аргумента " - Трава Саттер, Исключительный C ++, p120

1 голос
/ 22 декабря 2008

вам нужно явно привести ваш аргумент к x или y

doIt (new Both ());

так что добавьте ...

(X *) или (Y *)

как ...

doIt ((X *) new Both ());

0 голосов
/ 22 декабря 2008

AFAIK компилятор C ++ всегда выбирает наиболее близкое и конкретное соответствие, которое он может определить во время компиляции.

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

...