Программирование на C и C++

Онлайн справочник программиста на C и C++

Перегрузка функций и неопределенность

При перегрузке функций возможно появление ошибок такого типа, какие нам раньше не встре­чались. Можно создать ситуацию, в которой компилятор не сможет выбрать между двумя или более перегруженными функциями. Когда такое происходит, говорят, что ситуация неопределен­
на, двусмысленна (ambiguous). Неопределенные инструкции являются ошибками, и программа, содержащая неопределенности, не будет откомпилирована.

Основная причина, которая может вызывать неопределенность, связана с автоматическим пре­образованием типов в С++. С++ автоматически пытается преобразовать аргументы, использован­ные при вызове функции, к типу аргументов интерфейса функции. В качестве примера рассмот­рим следующий фрагмент:

int myfunc(double d);
...
cout << myfunc('c'); // ошибки не происходит из-за преобразования

Как указано в комментарии, данный код не является ошибочным, потому что С автоматически конвертирует символ с в его эквивалент типа double. В С++ только немногие из подобных преоб­разований запрещены. Хотя автоматическое преобразование типов является очень удобным, оно служит наиболее распространенной причиной неопределенности. Например, рассмотрим следу­ющую программу:

#include <iostream.h>
float myfunc(float i);
double myfunc(double i);
int main()
{
cout << myfunc (10.1) << " "; // неопределенности нет, вызов myfunc(double)
cout << myfunc(10); // неопределенность
return 0;
}
float myfunc (float i)
{
return i;
}
double myfunc(double i)
{
return -i;
}

Здесь функция myfunc() перегружена, так что она может иметь в качестве аргументов переменные типа float или double. Первая строка в функции main() не вызывает неопределенности, поскольку число 10.1 автоматически преобразуется в С++ к типу double и вызывается функция myfunc() с аргументом типа double. Однако когда функция myfunc() вызывается с использованием числа 10, возникает неопределенность, поскольку компилятор не может определить, приводить это число к типу float или типу double. Это вызывает сообщение об ошибке, и программа не компилируется.

Как показывает предыдущий пример, неопределенность вызвана не перегрузкой функции myfunc(), а вызовом этой функции с неопределенным типом аргумента. Иными словами, ошибка вызвана не перегрузкой функции myfunc(), а особенностями ее вызова.

Ниже приведен другой пример неопределенности, вызванной автоматическим преобразовани­ем типа в языке С++:

#include <iostream.h>
char myfunc (unsigned char ch);
char myfunc(char ch);
int main()
{
cout << myfunc('с'); // вызов myfunc(char)
cout << myfunc(88) << " "; // неопределенность
return 0;
}
char myfunc (unsigned char ch)
{
return ch-1;
}
char myfunc(char ch)
{
return ch+1;
}

В С++ unsigned char и char не являются наследственно двусмысленными. Тем не менее, когда функция myfunc() вызывается с числом 88, компилятор не знает, какую из функций вызывать. Иными словами, следует ли 88 преобразовывать к char или к unsigned char?

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

#include <iostream.h>
int myfunc (int i);
int myfunc (int i, int j = 1);
int main()
{
cout << myfunc (4, 5) << " "; // неопределенности нет
cout << myfunc(10); // неопределенность
return 0;
}
int myfunc (int i)
{
return i;
}
int myfunc (int i, int j)
{
return i*j;
}

Здесь при первом вызове функции myfunc() указываются два аргумента, так что неопределенность не возникает и вызывается функция myfunc(int i, int j). Однако, когда вызывается вторая функция myfunc(), возникает двусмысленность, поскольку компилятор не знает, вызывать ли функцию myfunc() с одним аргументом, или же использовать функцию с двумя аргументами, у которой второй аргумент принимает значение по умолчанию.