14.9.3. Подбор функций и перегруженные операторы

Перегруженные операторы — это перегруженные функции. При выявлении, который из встроенных или перегруженных операторов применяется для данного выражения, используется обычный подбор функции (см. раздел 6.4). Однако, когда в выражении используется функция оператора, набор функций-кандидатов шире, чем при вызове функций, использующих оператор вызова. Если объект а имеет тип класса, то выражение a sym b может быть следующим:

a.operator sym(b);  // класс а содержит оператор sym как функцию-член

operator sym(a, b); // оператор sym - обычная функция 

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

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

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

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

Определим, например, оператор суммы для класса SmallInt:

class SmallInt {

 friend

 SmallInt operator*(const SmallInt&, const SmallInt&);

public:

 SmallInt(int = 0);                   // преобразование из int

 operator int() const { return val; } // преобразование в int

private:

 std::size_t val;

};

Этот класс можно использовать для суммирования двух объектов класса SmallInt, но при попытке выполнения смешанных арифметических операций возникнет проблема неоднозначности:

SmallInt s1, s2;

SmallInt s3 = s1 + s2; // использование перегруженного оператора +

int i = s3 + 0;        // ошибка: неоднозначность

Первый случай суммирования использует перегруженную версию оператора + для суммирования двух значений типа SmallInt. Второй случай неоднозначен, поскольку 0 можно преобразовать в тип SmallInt и использовать версию оператора + класса SmallInt либо преобразовать объект s3 в тип int и использовать встроенный оператор суммы для типа int.

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

Упражнения раздела 14.9.3

Упражнение 14.52. Какой из операторов operator+, если таковые вообще имеются, будет выбран для каждого из следующих выражений суммы? Перечислите функции-кандидаты, подходящие функции и преобразования типов для аргументов каждой подходящей функции:

struct LongDouble {

 // оператор-член operator+ только для демонстрации;

 // обычно он не является членом класса

 LongDouble operator+(const SmallInt&); // другие члены как в p. 14.9.2

};

LongDouble operator+(LongDouble&, double);

SmallInt si;

LongDouble ld;

ld = si + ld;

ld = ld + si;

Упражнение 14.53. С учетом определения класса SmallInt определите, допустимо ли следующее выражение суммы. Если да, то какой оператор суммы используется? В противном случае, как можно изменить код, чтобы сделать его допустимым?

SmallInt s1;

double d = s1 + 3.14;

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК