Почему эта перегруженная функция неоднозначна?

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

#include <iostream>
using namespace std;
double volume(int a){
    return a*a*a;
}
double volume(int a, int b, int c){
    return b*c*a;

}
double volume(int a, int b){
    return a*a*b;
}
double volume(double a, double b, double c){
    return a*b*c*2.5;
}
int main(){
    cout<<volume(2)<<endl;
    cout<<volume(2,3)<<endl;
    cout<<volume(2,2,3)<<endl;
    cout<<volume(2.0,9.7,3)<<endl;//error here
    return 0;
}

Ошибка, которую я получил, была такой

ошибка: вызов перегруженного ‘volume(double, double, int)’ неоднозначен


person Sailesh Dahal    schedule 21.07.2018    source источник
comment
Что должен думать компилятор о 3?   -  person zdf    schedule 21.07.2018
comment
Как компилятор должен знать, хотите ли вы неявно преобразовать 3 (целое число, поскольку десятичной точки нет) в двойное число или преобразовать 2.0 и 9.7 в целые числа?   -  person BessieTheCookie    schedule 21.07.2018


Ответы (4)


Компилятор был сбит с толку, когда вы вызывали volume(double, double, int), так как компилятор должен преобразовывать двойные числа в целые числа и вызывать volume(int, int, int) или должен преобразовывать целые числа в двойные числа и вызывать volume(double, double, double).


Для продвинутых читателей

Как работает перегрузка?

Есть 3 шага к разрешению перегрузки.

  1. Поиск функций-кандидатов : Функции с тем же именем, что и у вызываемой функции, называются функциями-кандидатами. В вашем случае все функции с именем volume являются функциями-кандидатами, потому что вы вызвали volume(..)

  2. Поиск жизнеспособных функций. Чтобы быть жизнеспособной, функция должна пройти два теста. Во-первых, функция должна иметь столько же параметров, сколько аргументов в вызове. (кроме аргументов по умолчанию). Во-вторых, каждый аргумент должен соответствовать типу соответствующих параметров или быть конвертируемым в него. В вашем случае volume(int,int,int) and volume(double, double, double) являются жизнеспособными функциями.

  3. Поиск наилучшего соответствия, если таковое имеется. На последнем этапе разрешения перегрузки определяются жизнеспособные функции, которые лучше всего соответствуют аргументам. Лучшее здесь означает -- точное совпадение типов лучше, чем совпадение, требующее преобразования типа аргумента в тип параметра. В вашем случае лучшего совпадения нет. Ваш вызов volume(double, double, int) не соответствует ни одной жизнеспособной функции.


Проблема с несколькими параметрами

Давайте рассмотрим этот пример более простого случая

void foo(int x, int y);
void foo(double x, double y);

Предположим, мы делаем вызов как

foo(4.2, 5) //double and int

Вышеупомянутые два foo являются функциями-кандидатами (то же имя).

И обе они также являются жизнеспособными функциями, потому что вызов может быть сделан путем преобразования, и оба принимают 2 аргумента.

Так что теперь компилятор решает аргумент за аргументом, какая функция лучше всего подходит.

Есть совпадение, если существует только и только одна функция, для которой:

1. Соответствие для каждого аргумента не хуже, чем соответствие, требуемое любой другой жизнеспособной функцией

2. Существует по крайней мере один аргумент, для которого соответствие лучше, чем соответствие, предоставляемое любой другой жизнеспособной функцией

Таким образом, когда компилятор проверяет первый аргумент по первому параметру жизнеспособной функции, он выбирает, что foo(double, double) является лучшим соответствием, но когда он проверяет второй аргумент, он обнаруживает, что foo(int, int) является лучшим соответствием.

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


Решение

Чтобы устранить такую ​​двусмысленность, вам нужно убедиться, что компилятор может найти наилучшие совпадения. Поэтому вам нужно явно преобразовать аргументы. В вашем случае вы можете либо преобразовать двойные числа в целые, либо последнее целое в двойное. Этот вызов не будет двусмысленным:

volume(2.0, 9.7, static_cast<double>(3));

Почему static_cast?? Ну, в общем, вы можете напрямую написать 3.0, и он будет работать нормально, но вы не можете сделать это с некоторыми переменными, скажем

int x = 10;
float a= 2.1, b=4.8;
for(int t=1;t<=x;t++){
   volume(a,b,static_cast<double>(t));
   //do something with t here 
}

ПРИМЕЧАНИЕ

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

person coder3101    schedule 21.07.2018

Сканер GCC токенизирует 3 как int вместо double

Изменить volume(2.0,9.7,3) на volume(2.0,9.7,3.0)

Зависит от этих двух функций:

double volume(double a, double b, double c)
double volume(int a, int b, int c)

Вы можете иметь volume(2.0,9.7,3.0) или volume(2,9,3)

Комментарий ниже, если были какие-либо проблемы

person Vala Khosravi    schedule 21.07.2018
comment
почему это не работает, когда я меняю весь двойной тип на float и вызываю функцию как громкость (2.0,9.7,3.0) - person Sailesh Dahal; 21.07.2018
comment
Проверьте это на @SaileshDahal - person Vala Khosravi; 21.07.2018
comment
@Sailesh - потому что у компиляции есть варианты: преобразовать все три аргумента из double в float или преобразовать все три аргумента из double в int, чтобы выбрать, какую функцию вызывать. И оба варианта снова одинаково жизнеспособны. - person Peter; 21.07.2018

В volume(2.0,9.7,3) и 2.0, и 9.7 являются литеральными значениями типа double, но 3 имеет тип int.

Таким образом, у компилятора есть два одинаково жизнеспособных варианта среди предоставленных вами версий volume();

  • Преобразуйте два значения double в int и вызовите версию volume(), которая принимает три аргумента int.

  • Преобразуйте значение int в double и вызовите версию volume(), которая принимает три аргумента double.

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

Чтобы сделать вещи однозначными для компилятора, вам нужно устранить двусмысленность, убедившись, что все три аргумента имеют один и тот же тип, поскольку выбор делается между двумя функциями, каждая из которых имеет три аргумента одного и того же типа (double или int). Один из способов — предоставить три значения int (например, volume(2,9,3)) или три значения double (например, volume(2.0, 9.7, 3.0)).

person Peter    schedule 21.07.2018

компилятор пытается выполнить некоторое неявное преобразование (double в int), у вас здесь 2 совпадения

double volume(int a, int b, int c)
double volume(double a, double b, double c)

изменить cout<<volume(2.0,9.7,3)<<endl на cout<<volume(2.0,9.7,3.0)<<endl

person moshe    schedule 21.07.2018
comment
Всегда старайтесь включать правильные блоки кода с примером, чтобы OP или кто-либо еще, приходящий сюда, мог ясно понять. Поздравляю с первым ответом :) - person Amruth Pillai; 21.07.2018