Выбрать главу

15.11.3. Устоявшие функции

Устоявшей называется функция из множества кандидатов, которая может быть вызвана с данными фактическими аргументами. Чтобы она устояла, должны существовать неявные преобразования между типами фактических аргументов и формальных параметров. Например: class myClass { public: void mf( double ); void mf( char, char = '\n' ); static void mf( int* ); // ... }; int main() { myClass mc; int iobj; mc.mf( iobj ); // какая именно функция-член mf()? Неоднозначно }

В этом фрагменте для вызова mf() из main() есть две устоявшие функции:

void mf( double );

void mf( char, char = '\n' );

* mf(double) устояла потому, что у нее только один параметр и существует стандартное преобразование аргумента iobj типа int в параметр типа double;

* mf(char,char) устояла потому, что для второго параметра имеется значение по умолчанию и существует стандартное преобразование аргумента iobj типа int в тип char первого формального параметра.

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

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

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

class myClass {

public:

static void mf( int );

char mf( char );

};

int main() {

char cobj;

myClass::mf( cobj ); // какая именно функция-член?

}

Здесь функция-член mf() вызывается с указанием имени класса и оператора разрешения области видимости myClass::mf(). Однако не задан ни объект (с оператором "точка"), ни указатель на объект (с оператором "стрелка"). Несмотря на это, нестатическая функция-член mf(char) все же включается в множество устоявших наряду со статическим членом mf(int).

Затем процесс разрешения перегрузки продолжается: на основе ранжирования преобразований типов, примененных к фактическим аргументам, чтобы выбрать наилучшую из устоявших функций. Аргумент cobj типа char точно соответствует формальному параметру mf(char) и может быть расширен до типа формального параметра mf(int). Поскольку ранг точного соответствия выше, то выбирается функция mf(char).

Однако эта функция-член не является статической и, следовательно, вызывается только через объект или указатель на объект класса myClass с помощью одного из операторов доступа. В такой ситуации, если объект не указан и, значит, вызов функции невозможен (как раз наш случай), компилятор считает его ошибкой.

Еще одна особенность функций-членов, которую надо принимать во внимание при формировании множества устоявших функций, – это наличие спецификаторов const или volatile у нестатических членов. (Они рассматривались в разделе 13.3.) Как они влияют на процесс разрешения перегрузки? Пусть в классе myClass есть следующие функции-члены:

class myClass {

public:

static void mf( int* );

void mf( double );

void mf( int ) const;

// ...

};

Тогда и статическая функция-член mf(int*), и константная функция mf(int), и неконстантная функция mf(double) включаются в множество кандидатов для показанного ниже вызова. Но какие из них войдут в множество устоявших?

int main() {

const myClass mc;

double dobj;

mc.mf( dobj ); // какая из функций-членов mf()?

}

Исследуя преобразования, которые надо применить к фактическим аргументам, мы обнаруживаем, что устояли функции mf(double) и mf(int). Тип double фактического аргумента dobj точно соответствует типу формального параметра mf(double) и может быть приведен к типу параметра mf(int) с помощью стандартного преобразования.

Если при вызове функции-члена используются операторы доступа "точка" или"стрелка", то при отборе функций в множество устоявших принимается во внимание тип объекта или указателя, для которого вызвана функция.

mc – это константный объект, для которого можно вызывать только нестатические константные функции-члены. Следовательно, неконстантная функция-член mf(double) исключается из множества устоявших, и остается в нем единственная функция mf(int), которая и вызывается.