50
2. Если слева указан пользовательский тип, компилятор ищет перепруженный оператор в форме
функции данного класса, подходящей для всей сигнатуры подвыражения оператора. Если такой
оператор будет найден, он используется при компиляции.
3. Если все остальные варианты испробованы, компилятор ищет перегрузку в форме внешней
функции.
Неоднозначность может возникнуть лишь в том случае, если она присутствует в левостороннем классе
или в глобальном пространстве, и никогда — из-за совпадения перегруженных операторов в форме
функции класса и внешней функции. При наличии неоднозначности сообщение об
ошибке выдается
лишь после вашей попытки реально использовать оператор. Так компилятор внушает ложное чувство
безопасности и ждет, пока вы утратите бдительность, чтобы огреть вас дубиной по голове.
Виртуальные операторы
Операторы классов можно объявлять виртуальными, как и все остальные функции классов.
Компилятор динамически обрабатывает перегруженный левосторонний оператор, как и любую другую
функцию класса. Такая возможность особенно полезна в ситуациях, когда вы пытаетесь создать
семейство классов, но открываете внешнему миру лишь их общий базовый класс. С точки зрения
синтаксиса все выглядит просто, но логика программы может стать довольно запутанной. Виртуальные
операторы являются одной из важнейших тем части 3, поэтому сейчас мы не будем вдаваться в
подробности.
Оператор ->
Оператор
->
занимает особое место среди операторов. Для начала рассмотрим его базовый синтаксис.
class Pointer {
private:
Foo*
f;
public:
Pointer(Foo* foo) : f(foo) {}
Foo* operator->() const { return f; }
};
Pointer p(new Foo);
p->MemberOfFoo();
В приведенном фрагменте
р
используется для косвенного
вызова функции класса
Foo
. Компилятор
интерпретирует любой указатель на структуру или класс (
*
-переменная) как базовый тип
>
, а для всех
базовых типов указателей существует встроенный оператор
->
. Встретив
->
, компилятор смотрит на
левостороннее выражение; если оно представляет собой указатель па структуру или указатель на класс,
для обращения к членам используется встроенный оператор
->
. Если левостороннее выражение
представляет собой пользовательский тип, этот тип должен перегрузить оператор
->
. Перегруженный
вариант должен возвращать либо указатель на структуру/класс, либо какой-нибудь другой
пользовательский тип, который также перегружает оператор
->
.
Если возвращаемое значение
относится к пользовательскому типу, компилятор заменяет левостороннее выражение возвращаемым
значением оператора
->
(в нашем примере
Foo*
) и продолжает свои попытки до тех пор,
пока не
доберется до встроенного указателя. Таким образом, следующее двухшаговое косвенное обращение
также будет работать.
class Pointer2 {
private:
Pointer
p;
public:
Pointer(Foo* foo) : p(foo) {}
Pointer operator->() const { return p; }
};
Pointer2 p(new Foo);
p->MemberOfFoo();