Подтвердить что ты не робот

Почему компилятор может перегружать функции передачей по ссылке и передачей по значению

Я думал, что во время перегрузки компилятор проверяет, являются ли формальные аргументы одного и того же типа. Например:

void a(int x)
void a(double x)

может перегружаться просто потому, что два "х" имеют тип разности.

Однако существуют ли следующие два типа:

void f(int y)
void f(int& y)

Я понимаю, что это PBV и другой PBR. Но второй у имеет тип "int", а также правильно? Почему он успешно компилируется?

P.S. Я замечаю, что, хотя он компилируется, он не работает, хотя и сообщает ошибку времени выполнения неоднозначности.

4b9b3361

Ответ 1

Функции в целом могут быть перегружены на основе:

  • Число аргументов
  • Тип аргументов
  • Последовательность аргументов

Пример компилируется, потому что он удовлетворяет второму критерию.
int и int & - разные типы данных.


Рассмотрим следующий пример:

void foo(int i);
void foo(int& i);
void foo(int i){}
void foo(int& i){}
int main()
{
     return 0;
}  

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

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

void foo(int i);
void foo(int& i);
void foo(int i){}
void foo(int& i){}
int main()
{
    foo(20); 
    return 0;
}  

Но,

void foo(int i);
void foo(int& i);
void foo(int i){}
void foo(int& i){}
int main()
{
    int i = 10;
    foo(i);
    return 0;
}  

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

Ответ 2

Это зависит. Если вы не вызываете эти функции из своего кода, компилятор может их оптимизировать. Если это не так, вы можете открыть файл .obj с помощью средства просмотра текста и поиска имени функции (дайте ему более уникальное имя, чем f, например floop:-)), вы увидите два искалеченных имени используя ваше основное имя функции.

Но если вы попытаетесь их использовать, вы заметите разницу. Если вы вызываете

f(5);

Компилятор может использовать только версию f(int y), потому что невозможно передать константу по ссылке. Но если вы делаете

int b = 10;
f(b);

Затем компилятор выдает ошибку amibuity, так как обе формы f могут быть вызваны таким образом.

Ответ 3

Возьмем вызов f (x), где x - int.

$13.3.3.1.4 - "Когда параметр ссылочного типа связывается напрямую (8.5.3) к выражению аргумента, неявная последовательность преобразования преобразование идентичности, если выражение аргумента не имеет типа это производный класс типа параметра, и в этом случае неявная последовательность преобразования представляет собой преобразование с производной базой"

Следовательно, f (int &) является точным совпадением, а также f (int), поскольку оба являются преобразованиями идентичности. Поэтому двусмысленность

Теперь возьмем вызов 'f (2)'

Это прекрасно, потому что 'f (int &)' не является совпадением вообще, поскольку rvalues ​​не связывают с не-const lvalues. Поэтому никакая двусмысленность

Таким образом, стандарт позволяет 'f (T)' abd 'f (T &)' формировать набор перегрузки.

Ответ 4

Во-первых, неоднозначность всегда сообщается во время компиляции.

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

void f(int y)
{
}
void f(int& y)
{
}


int main ()
{
    int a = 10;

    f (a); // ERROR: ambiguous call
    f (10); // OK
}

В первом случае возникает ошибка, так как a может быть передана как копия, так и ссылка. Во втором случае ошибки не будет, потому что int литерал не может быть передан неконстантной ссылкой