Перегрузка и выбор функций
Перегруженные функции являются важным дополнением C++. Конкретная функция выбирается в зависимости от соответствия списка аргументов при вызове функции списку параметров в объявлении функции. Когда вызывается перегруженная функция, компилятор должен иметь алгоритм для выбора надлежащей функции. Алгоритм, который выполняет этот выбор, зависит от того, преобразования какого типа присутствуют. Наилучшее соответствие должно быть уникальным. Оно должно быть лучшим по крайней мере для одного аргумента и так же хорошо, как остальные соответствия, для всех других аргументов.
Ниже приведен алгоритм соответствия для каждого аргумента.
Алгоритм выбора перегруженной функции
1. Использовать строгое соответствие (если возможно).
2. Попробовать стандартное повышение типа (см. раздел 2.5 на стр. 46).
3. Попробовать стандартное преобразование типа.
4. Попробовать определяемое пользователем преобразование.
5. Использовать, если возможно, соответствие эллипсису (подразумеваемому аргументу).
Стандартное повышение типа (promotion) лучше, чем остальные стандартные преобразования. Повышение — это преобразования float в double, а также bool, char, short или enum в int. Кроме того, к стандартным преобразованиям относятся преобразования указателей.
Несомненно, строгое соответствие является наилучшим. Для достижения такого соответствия могут использоваться приведения (cast). Компилятор не справится с двусмысленной ситуацией. Поэтому не следует полагаться на тонкие различия в типах и неявные преобразования, которые делают перегруженную функцию неясной. Если вы сомневаетесь, используйте явные преобразования для обеспечения строгого соответствия.
Напишем перегруженную функцию greater () (больше) и будем следовать нашему алгоритму для разных вызовов. В этом примере имеется пользовательский тип rational (рациональный).
В файле rational.срр
//Перегруженные функции
class rational{
public:
rational (int n=0) : a(n), q(1) {}
rational (int i, int j) : a(i), q(j) {}
rational (double r) : q(BIG), a(r * BIG) {}
void print () const {cout << a << “/” << q;}
operator double() {return static_cast<double> (a)/q;}
private:
long a, q;
enum {BIG = 100};
};
inline int greater(int i, int j)
{ return ( i > j ? i : j); }
inline double greater(double x, double y)
{ return ( x > у ? x : y); }
inline rational greater(rational w, rational z)
{ return (w > z ? w : z); }
int main()
{
int i = 10, j = 5;
float x = 7.0;
double y = 14.5;
rational w(10), z(3.5), zmax;
cout << “\ngreater(“ << i << “, “ << j << “) = ”
<< greater(i, j) ;
cout << “\ngreater(“ << x << “, “ << y << “) = “
<< greater(x, y) ;
cout << “\ngreater(“ << i << “, “ ;
z.print() ;
cout << “) = “ << greater(static_cast<rational>(i), z) ;
zmax = greater(w, z) ;
cout << “\ngreater(“;
w.print () ;
cout << “ , “;
z.print() ;
cout << “) = “ ;
zmax.print();
}
Вот что выведет эта программа:
greater(10, 5) = 10
greater(7, 14.5) = 14.5
greater(10, 350 / 100) = 10
greater(10 / 1, 350 / 100) =10/1
Применялись различные правила преобразования — и явные, и неявные.