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

struct s {

 virtual f();

 //…

};

struct ss: public s {

 f();

 //…

};

void f()

{

 s a;

 ss b;

 a = b; // на самом деле выполняется a.s::operator=(b)

 b = a; // ошибка

 a.f(); // вызов s::f

 b.f(); // вызов ss::f

 (s&)b = a; // присваивание a b

  // на самом деле выполняется ((s&)b).s::operator=(a)

 b.f(); // все еще вызов ss::f

}

Вызов a.f() приведет к вызову s::f() (как и должно быть для объекта класса s (§R.10.2)), а вызов b.f() приведет к вызову ss::f() (как и должно быть для объекта класса ss).

R.13 Перегрузка

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

double abs(double);

int abs(int);

abs(1); // вызов abs(int)

abs(1.0); // вызов abs(double)

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

int f(int i)

{

 //…

}

int f(int& r) // ошибка: типы функций

{ // недостаточно различны

 //…

}

Аналогично, поскольку для любом типе T для самого T, const T и volatile T допустимо одно и то же множество инициализирующих значений, функции, типы параметров которых отличаются только указанной спецификацией, не могут иметь одинаковые имена. Однако, различить const T&, volatile T& и просто T& можно, поэтому допустимы определения функций с одним именем, которые различаются только в указанном отношении. Аналогично, допустимы определения функций с одним именем, типы параметров которых различаются только как типы вида const T*, volatile T* и просто T*.

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

Не могут иметь одинаковые имена функции-члены, одна из которых статическая, а другая нет (§R.9.4).

С помощью конструкции typedef не создаются новые типы, а только определяется синоним типа (§R.7.1.3), поэтому функции, которые отличаются только за счет использования типов, определенных с помощью typedef, не могут иметь одинаковые имена. Приведем пример:

typedef int Int;

void f(int i) {/*… */}

void f(Int i) {/*… */} // ошибка: переопределение f

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

enum E { a };

void f(int i) {/*… */}

void f(E i) {/*… */}

Типы параметров, которые различаются только тем, что в одном используется указатель *, а в другом массив [], считаются идентичными. Напомним, что для типа параметра важны только второй и последующие индексы многомерного массива (§R.8.2.4). Подтвердим сказанное примером:

f(char*);

f(char[]); // идентично f(char*);

f(char[7]); // идентично f(char*);

f(char[9]); // идентично f(char*);

g(char(*)[10]);

g(char[5][10]); // идентично g(char(*)[10]);

g(char[7][10]); // идентично g(char(*)[10]);

g(char(*)[20]); // отлично от g(char(*)[10]);

R.13.1 Сопоставление описаний

Два описания функций с одинаковыми именами относятся к одной и той же функции, если они находятся в одной области видимости и имеют идентичные типы параметров (§R.13). Функция-член производного класса относится к иной области видимости, чем функция-член базового класса с тем же именем. Рассмотрим пример:

class B {

public:

 int f(int);

};

class D: public B {

public:

 int f(char*);

};

Здесь D::f(char*) скорее скрывает B::f(int), чем перегружает эту функцию.

void h(D* pd)

{

 pd-›f(1); // ошибка: D::f(char*) скрывает B::f(int)

 pd-›B::f(1); // нормально

 pd-›f("Ben"); // нормально, вызов D::f

}

Функция, описанная локально, находится в иной области видимости, чем функция с файловой областью видимости.

int f(char*);

void g()

{

 extern f(int);

 f("asdf"); // ошибка: f(int) скрывает f(char*) поэтому

  // в текущей области видимости нет f(char*)

}

Для разных вариантов перегруженной функции-члена можно задать разные правила доступа, например:

class buffer {

private:

 char* p;

 int size;

protected:

 buffer(int s, char* store) { size = s; p = store; }

 //…

public:

 buffer(int s) { p = new char[size = s]; }

};

R.13.2 Сопоставление параметров

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