Перегрузка операций
7.1 Введение
Если я выбираю слово, оно значит только то, что я решу, ни больше и ни меньше.
Обычно в программах используются объекты, являющиеся конкретным представлением абстрактных понятий. Например, в С++ тип данных int вместе с операциями +, -, *, / и т.д. реализует (хотя и ограниченно) математическое понятие целого. Обычно с понятием связывается набор действий, которые реализуются в языке в виде основных операций над объектами, задаваемых в сжатом, удобном и привычном виде.
К сожалению, в языках программирования непосредственно представляется только малое число понятий. Так, понятия комплексных чисел, алгебры матриц, логических сигналов и строк в С++ не имеют непосредственного выражения. Возможность задать представление сложных объектов вместе с набором операций, выполняемых над такими объектами, реализуют в С++ классы. Позволяя программисту определять операции над объектами классов, мы получаем более удобную и традиционную систему обозначений для работы с этими объектами по сравнению с той, в которой все операции задаются как обычные функции. Приведем пример:
class complex {
   double re, im;
 public:
   complex(double r, double i) { re=r; im=i; }
   friend complex operator+(complex, complex);
   friend complex operator*(complex, complex);
};Здесь приведена простая реализация понятия комплексного числа, когда оно представлено парой чисел с плавающей точкой двойной точности, с которыми можно оперировать только с помощью операций + и *.
Интерпретацию этих операций задает программист в определениях функций с именами operator+ и operator*. Так, если b и c имеют тип complex, то b+c означает (по определению) operator+(b,c). Теперь можно приблизиться к привычной записи комплексных выражений:
void f()
 {
   complex a = complex(1,3.1);
   complex b = complex(1.2,2);
   complex c = b;
   a = b+c;
   b = b+c*a;
   c = a*b+complex(1,2);
 }Сохраняются обычные приоритеты операций, поэтому второе выражение выполняется как b=b+(c*a), а не как b=(b+c)*a.
7.2 Операторные функции
Можно описать функции, определяющие интерпретацию следующих операций:
+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [] () new delete
Последние пять операций означают: косвенное обращение (  7.9),
 индексацию (
 7.9),
 индексацию (  7.7), вызов функции (
 7.7), вызов функции (  7.8), размещение в свободной
 памяти и освобождение (
 7.8), размещение в свободной
 памяти и освобождение (  3.2.6). Нельзя изменить приоритеты этих
 операций, равно как и синтаксические правила для выражений. Так,
 нельзя определить унарную операцию %,  также как и бинарную операцию !. Нельзя ввести новые лексемы для обозначения операций, но если
 набор операций вас не устраивает, можно воспользоваться привычным
 обозначением вызова функции. Поэтому используйте pow(), а не **.
 3.2.6). Нельзя изменить приоритеты этих
 операций, равно как и синтаксические правила для выражений. Так,
 нельзя определить унарную операцию %,  также как и бинарную операцию !. Нельзя ввести новые лексемы для обозначения операций, но если
 набор операций вас не устраивает, можно воспользоваться привычным
 обозначением вызова функции. Поэтому используйте pow(), а не **.
Эти ограничения можно счесть драконовскими, но более свободные правила легко приводят к неоднозначности. Допустим, мы определим операцию ** как возведение в степень, что на первый взгляд кажется очевидной и простой задачей. Но если как следует подумать, то возникают вопросы: должны ли операции ** выполняться слева направо (как в Фортране) или справа налево (как в Алголе)? Как интерпретировать выражение a**p как a*(*p) или как (a)**(p)?
Именем операторной функции является служебное слово operator, за которым идет сама операция, например, operator<<. Операторная функция описывается и вызывается как обычная функция. Использование символа операции является просто краткой формой записи вызова операторной функции:
void f(complex a, complex b)
{
  complex c = a + b;           // краткая форма
  complex d = operator+(a,b);  // явный вызов
}С учетом приведенного описания типа complex инициализаторы в этом примере являются эквивалентными.
7.2.1 Бинарные и унарные операции
Бинарную операцию можно определить как функцию-член с одним
 параметром, или как глобальную функцию с двумя параметрами. Значит,
 для любой бинарной операции @  выражение aa @ bb  интерпретируется
 либо как aa.operator@(bb), либо как operator@(aa,bb). Если определены обе
 функции, то выбор интерпретации происходит по правилам сопоставления
 параметров ( 4.13.2). Префиксная или постфиксная унарная операция
 может определяться как функция-член без параметров, или как глобальная
 функция с одними параметром. Для любой префиксной унарной операции @  выражение @aa  интерпретируется либо как aa.operator@(), либо как operator@(aa). Если определены обе функции, то выбор интерпретации
 происходит по правилам сопоставления параметров (
 4.13.2). Префиксная или постфиксная унарная операция
 может определяться как функция-член без параметров, или как глобальная
 функция с одними параметром. Для любой префиксной унарной операции @  выражение @aa  интерпретируется либо как aa.operator@(), либо как operator@(aa). Если определены обе функции, то выбор интерпретации
 происходит по правилам сопоставления параметров ( 4.13.2). Для
 любой постфиксной унарной операции @  выражение aa@  интерпретируется
 либо как aa.operator@(int), либо как operator@(aa,int). Подробно
 это объясняется в
 4.13.2). Для
 любой постфиксной унарной операции @  выражение aa@  интерпретируется
 либо как aa.operator@(int), либо как operator@(aa,int). Подробно
 это объясняется в  7.10. Если определены обе функции, то выбор
 интерпретации происходит по правилам сопоставления параметров
 (
 7.10. Если определены обе функции, то выбор
 интерпретации происходит по правилам сопоставления параметров
 ( 13.2). Операцию можно определить только в соответствии с
 синтаксическими правилами, имеющимися для нее в грамматике С++.
 13.2). Операцию можно определить только в соответствии с
 синтаксическими правилами, имеющимися для нее в грамматике С++.
В частности, нельзя определить % как унарную операцию, а + как тернарную. Проиллюстрируем сказанное примерами:
class X {
  // члены (неявно используется указатель `this'):
  X* operator&();        // префиксная унарная операция &
                         // (взятие адреса)
  X operator&(X);        // бинарная операция & (И поразрядное)
  X operator++(int);     // постфиксный инкремент
  X operator&(X,X);      // ошибка: & не может быть тернарной
  X operator/();         // ошибка: / не может быть унарной
};
                    // глобальные функции (обычно друзья)
X operator-(X);          // префиксный унарный минус
X operator-(X,X);        // бинарный минус
X operator--(X&,int);    // постфиксный декремент
X operator-();           // ошибка: нет операнда
X operator-(X,X,X);      // ошибка: тернарная операция
X operator%(X);          // ошибка: унарная операция %Операция [] описывается в  7.7, операция () в
 7.7, операция () в  7.8, операция ->
 в
 7.8, операция ->
 в  7.9, а операции ++ и -- в
 7.9, а операции ++ и -- в  7.10.
 7.10.
7.2.2 Предопределенные свойства операций
Используется только несколько предположений о свойствах пользовательских операций. В частности, operator=, operator[], operator() и operator-> должны быть нестатическими функциями-членами. Этим обеспечивается то, что первый операнд этих операций является адресом.
Для некоторых встроенных операций их интерпретация определяется как комбинация других операций, выполняемых над теми же операндами.
Так, если a типа int, то ++a означает a+=1, что в свою очередь означает a=a+1. Такие соотношения не сохраняются для пользовательских операций, если только пользователь специально не определил их с такой целью. Так, определение operator +=() для типа complex нельзя вывести из определений complex::operator+() и complex operator=().
По исторической случайности оказалось, что операции = (присваивание), &(взятие адреса) и , (операция запятая) обладают предопределенными свойствами для объектов классов. Но можно закрыть от произвольного пользователя эти свойства, если описать эти операции как частные:
class X {
   // ...
private:
   void operator=(const X&);
   void operator&();
   void operator,(const X&);
   // ...
};
void f(X a, X b)
{
   a= b;   // ошибка: операция = частная
   &a;     // ошибка: операция & частная
   a,b     // ошибка: операция , частная
}С другой стороны, можно наоборот придать с помощью соответствующих определений этим операциям иное значение.
7.2.3 Операторные функции и пользовательские типы
Операторная функция должна быть либо членом, либо иметь по крайней мере один параметр, являющийся объектом класса (для функций, переопределяющих операции new и delete, это не обязательно). Это правило гарантирует, что пользователь не сумеет изменить интерпретацию выражений, не содержащих объектов пользовательского типа. В частности, нельзя определить операторную функцию, работающую только с указателями. Этим гарантируется, что в С++ возможны расширения, но не мутации (не считая операций =, &, и "," для объектов класса).
Операторная функция, имеющая первым параметр основного типа, не может быть функцией-членом. Так, если мы прибавляем комплексную переменную aa к целому 2, то при подходящем описании функции-члена aa+2 можно интерпретировать как aa.operator+(2), но 2+aa так интерпретировать нельзя, поскольку не существует класса int, для которого + определяется как 2.operator+(aa). Даже если бы это было возможно, для интерпретации aa+2 и 2+aa пришлось иметь дело с двумя разными функциями-членами. Этот пример тривиально записывается с помощью функций, не являющихся членами.
Каждое выражение проверяется для выявления неоднозначностей.
 Если пользовательские операции задают возможную интерпретацию
 выражения, оно проверяется в соответствии с правилами  R.13.2.
 R.13.2.
 
                             
