Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 68
Текст из файла (страница 68)
лед. Нельзя перегружать операции: (разрешение области видимости; 94.9.4, 910.2.4), (выбор члена класса; 95.7) и .* (выбор члена класса через указатель на классовые члены; 915.5). Правым операндом у этих операций является имя, а не значение, и обеспечивают они базовое средство доступа к членам. Возможность перегрузки этих операций привела бы к очень тонким ошибкам (5(гоцзгпзр, 1994). Тернарная условная операция з: 66.3.2) также запрещена к перегрузке. Также нельзя перегружать именованные операции в1ееоу (94.6) и ГуреЫ (915.4.4). Невозможно использовать не сутцествующие в языке С++ знаки операций, но можно воспользоваться функциями в случаях, когда существующие знаки операций не подходят.
Например, используйте розг() вместо **'. Такие ограничения могут показаться драконовскими, но более гибкая система обозначений легко приводит к неоднозначностям. Например, определение новой операции "* для обозначения возведения в степень кажется на первый взгляд очевидной и несложной в реализации, но это только на первый взгляд. Действительно, должна ли операция ** быть левоассоциативной (как в языке Гопгап), или правоассоциативной (как в язке А)яо1)? Как должно интерпретироваться выражение а**р — как а* (*р), или как (а) **(р)? имл функции-операции (орегагог~ипсггоп) начинается с ключевого слова орегагог, за которым следует знак перегружаемой операции, например орегагог«.
Функция-операция обьявляется и может быть вызвана так же, как и любая другая функция. Традиционная форма использования операции является сокращением явной функциональной формы вызова. Например: Глава ) ). Перегрузка операций 330 11.2.1. Бинарные и унарные операции Бинарную (двухоперандную) операцию можно перегрузить либо с помощью не- статической функции-члена с одним аргументом, либо с помощью глобальной функции с двумя аргументами. При этом для любой бинарной операции й выражение ааЮЬЬ интерпретируется либо как аа. орегагогй> (ЬЬ), либо как орегаго«Ф (аа, ЬЬ) .
Если определены оба варианта, то для выбора одного из них используются критерии разрешения перегрузки (57.4). Например: с1ат Х ( риЫ(с: ю1>1 орега1ог+ (тг) Х(и>1) > )) юЫ прего>ог-> (Х, Х); го(>1 орегагог+ (Х, с(оиЫе) го1Й((Х а) а->1; 1+а; а+1.О> ) УУ а. орега>о> ь(!) УУ норе«а>о«-~(Х(!), а) ,У::оре«асоге(а,1. 0) Унарную (однооперандную; префиксную нли постфиксную) операцию можно определить либо с помощью нестатнческой функции-члена без аргументов, либо с помощью глобальной функции с одним аргументом. Для любой префиксной унарной операции © выражение сапа интерпретируется либо как аа.
орегагоюФ (), либо как орегагогйр (аа) . Если определены оба варианта, то лля выбора одного из них используются критерии разрешения перегрузки (57.4). Для любой постфиксной унарной операции У выражение ааР интерпретируется либо как аа.орегаго«4Ь (тг), либо как орегаго«Ф (аа, (пг) . Более подробно это объясняется в 511.11. Если определены оба варианта, то для выбора одного из них используются критерии разрешения перегрузки 57.4).
Любая операция при перегрузке должна объявляться в соответствии с ее стандартным синтаксисом (5А.5). Например, пользователь не может определить унарную операцию . нли тернарную операцию +. Рассмотрим пример: У поптепфег (иясбопт Х орегагог- (Х) ! УУ нрефиксный упорный л>инус с1азз Х ( риЫ>с: У тетбет (ил>е>ои(ие Х орегагогй (); Х прего!ага (Х); Х орегаго~ч.+ (тг) „.
Х прего!ось (Х, Х); Х орегаьзг( (); неявный указатель >Ь!з)> УУпрефиксная упорная & (взятие адреса) >у бинарная & ("!Е) УУ постфиксный инкреиен>п (сас (Г!1,!1) У е«ог три операнда (терпарная операция) >Ус>го«. упорная операция У 11.2. Функции-операции 331 Р бинарный минус УУ постфиксный декремент Р епоп нет операнда ретгот; тернарноя операцич УУ естес: упорная операция >ть Х орегагог- (Х, Х) ) Х прего(от- — (Хь, )пг); Х орегагог- (); Х орегагог- (Х, Х, Х); Х прего(от% (Х); Перегрузка операции [) рассматривается в 311.8, операции () — в 3!1.9, операции -> — в З1!.10, операций -- и еь — в 311.11; перегрузка операций динамического выделения памяти и ее освобождения рассматривается в З6.2.6.2, 310.4.11 и 515.6.
11.2.2. Предопределенный сьгысл операций с(ат Х ( рг(юге: юЫ прего(от= (сопл Хь ) гоЫ прети(ось (); тоЫ прего(от, (сопл Хь) р... )' гоЫ г" (Х а, Х Ь ) ( а =Ь; ьа; а,Ь; ) р еп ок арета(от= рттеа(е реттог: арета(от& ргсга(е р епоп орегаьог, рмто(е А можно придать им новый смысл, переопределив их в классе соответствующим образом (естественно, в открытом режиме).
11.2.3. Операции и пользовательские типы Любая функция-операция должна быть либо членом класса, либо иметь по крайней мере один аргумент классового типо (за исключением функций орегагог пеп() и орегагог бе!еге ( ), участвующих в перегрузке операций пеггУ бе1еге). Это правило га- Относительно перегружаемых пользователем операций существует лишь ряд дополнительных правил.
В частности, орегагог- (), орегагог [1 (), орегагог () () и орегагог-> () обязаны быть нестотическими функциями-членами (и только ими); это гарантирует, что их первый операнд всегда 1ча!це (Э4.9.6). Для некоторых встроенных операций постулируется их эквивалентность определенной комбинации других операций. Например, если а имеет тип 1пг, то ++а означает то же, что и а += 1, что также эквивалентно а=а+ 1. Такие соотношения между операциями не поддерживаются для перегружаемых пользователем операций автоматическим образом, а только если пользователь позаботился об этом сам.
Например, компилятор не генерирует автоматически определение для 2:: орегагогь= () из одного того факта, что пользователь определил У:: орегагогч () и У:: орегагог= () . Исторически так сложилось, что операции = (операция присваивания), ь (операция взятия адреса) и, (операция следования; 36.2.2) имеют предопределенный смысл, когда применяются к классовым объектам. Их можно сделать недоступными для пользователей класса, если объявить операции закрытыми; Глава ) ). Перегрузка операций 332 рантирует, что пользователь не может изменить смысл выражения, если оно не содержит объектов пользовательских типов. В частности, нельзя изменить смысл операций, выполняющихся над указателями.
Можно сказать, что язык С++ является расширяемым, но он не подвержен мутациям (за исключением операций =, ь и, для классовых объектов). Функция-операция, принимающая первый аргумент встроенного типа (54.1.1), не может быть функцией-членом класса. Например, рассмотрим сложение комплексной переменной аа и целого числа 2: выражение ааь2 может при наличии соответствующего определения функции-члена рассматриваться как аа. орегагог-г (2), но выражение 2+аа не может никак интерпретироваться, ибо нет класса 1пг, для которого бьша бы определена операция + с возможность трактовки выражения как 2. орегагог+ (аа) .
Но даже, если бы такой класс и был, то все равно выражения аа+2 и 2ьаа требовали бы для интерпретации двух разных функций-членов. Из-за того, что компилятор не понимает смысла определяемых пользователем операций, он не может понять, что операция + коммутативна, и интерпретировать 2ьаа как ааь2. Рассмотренная проблема легко решается с помощью определения глобальных функций-операций (В))ль2, 311.5). Так как перечисления являются пользовательскими типами, то для них можно определять операции.
Например: епит Рау (лип, топ, гие, пед, Гйи, 7г1, вас); Раув орегагогче (Раув 0) ( ге(иго д = (ва(==И) 7 лип: Рау (дь1) ) Любое выражение проверяется на отсутствие неоднозначностей. Когда имеются предоставляемые пользователем операции, неоднозначности в выражениях устраняются по правилам из 37.4. 11.2.4. Операции и пространства имен Операции определяются либо в классах, либо в пространствах имен (в частности, в глобальном пространстве). Рассмотрим упрощенную версию ввода/вывода строк из стандартной библиотеки; д упрощенное лЫ патеврасе вгд ( сгат оигеат ( й... отгеать орегагог«(сопя( сааг*) )' ехсегп отгеагп соип с1аьв иг(пд ЗЗЗ 1) .2. Функции-операции озггеать орегагог« (оз1геать, сола згг(пяь) 1п1 таиг () ( спаг* р = "Нейе"; зи(.":зггшя 3 = "юог1'1"; зп(::соиг« р « "~ " << 5 « "( ~п" 1 ) Естественно, представленный код выводит строку Не11о, иог1а) Но почему? Заметьте, что я не стал делать доступными все средства нз йространства имен згз( при помощи директивы изгп8 патезрасе зи(1 Вместо этого я воспользовался префиксом А(:: для зптпя и соиг.
Таким образом я не стал засорять глобальное пространство имен и порождать ненужные зависимости. Для вывода С-строк (то есть с1(аг*) в классе зи1:: оззгеат имеется соответствующая функция-операция, так что по определению зга:: соиг«р означает з14:: сои(. орегагог«(р) Но для вывода строк типа зй::зита» в классе мз(:: оззгеат соответствующей функции-члена нет, так что зМ:: соиг«з означает орега(ог«(зМ:: сои1, з ) Операции, определенные в пространствах имен, различаются по типам операндов точно так же, как функции различаются по типам их аргументов (58.2.6). В частности, так как сощ определен в пространстве имен А(, то это пространство имен включается в процесс поиска подходящего определения для операции «. В результате, компилятор находит и использует зМ::орега1ог«(зи(:: озггеатз, сапог зЫ::згг(паз) Рассмотрим бинарную операцию Р.
Если х имеет тип Х, а у имеет тип У, то для выражения х(ву выполняется следующий поиск определений: ° Если Х класс, искать определение функции-члена орегагогь в классе Х или в его базовых классах. ° Искать объявление орегагогФ в контексте, окружающем х©у. ° Если Хопределен в пространстве имен Ж, искать объявление орегагогФ в )т( ° Если Уопределен в пространстве имен М, искать объявление орегагогФ в М. Если находятся несколько подходящих операций, то для выбора наилучшего соответствия (если оно существует) применяются критерии разрешения перегрузки Глава 11. Перегрузка операций 334 (87.4).
Рассмотренный механизм поиска выполняется только в том случае, когда один из операндов операции имеет пользовательский тип; при этом будут учтены операции приведения типа, определенные пользователем (81!.3.2, 511.4). Обратите внимание на то, что имя, введенное оператором (уреИеу; является лишь синонимом, а не пользовательским типом (54.9.7). Поиск определений унарных операций и разрешение неоднозначностей выполняются аналогично. Заметьте, что при поиске определений для операций никакого преимущества у функций-членов перед глобальными функциями нет; в этом состоит отличие от поиска именованных функций (88.2.6).