Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 65
Текст из файла (страница 65)
Но в таком случае при использовании в объявлении ее слсдует интерпретировать как двойную косвенность: спаг** р; // означает с)заг * * р Основная проблема здесь в том, что приоритет ** должен быть выше, чем у *, если мы хотим, чтобы выражение а/Ь**с интерпретировалось, как принято в математикс, те. а/ (Ь**с). С одной стороны, а/Ь**р в С означает (а/Ь) * (*р), а при новых правилах семантика изменилась бы на а/ (Ь* (*р) ) . Очевидно, что такой код в С и С++ встречается редко.
Можно было бы пойти на то, чтобы он перестал работать, если бы мы все же решили ввести оператор возведения в степень, особенно принимая во внимание, что компилятору совсем несложно выдать предупреждение в случае возможного изменения смысла. Меня позабавило, какую реакцию вызвало мое полусерьезное предложение об использовании **. И до сих пор меня не персстает удивлять тот нешуточный пыл, с которым обсуждаются отмечено, что в большинстве случаев н Рогггап-программах возведение в степень имеет вид а**п, где п — небольшое целое число; вместо этого вполне можно написать а*а или а*а*а.
И все жс изложу нскоторые технические соображения. Какой оператор лучше всего подошел бы в С++ на роль оператора возведения в степень? В С использованы все печатные символы из набора АВСП за исключением 9 и б, которые по разным причинам были сочтены непригодными. Рассматривались операторы '., -, *-, и даже одиночная (если хотя бы один операнд имеет не-интегральный тип). Однако 9, б, — и .' присутствуют не на всех национальных клавиатурах (см. раздел б.5.3.1)) кроме того многие считали, что ш и 8 плохо смотрятся в этой роли.
Лексемы и - воспринимаются С-программистами как чисключающсе или». Вдобавок следовало обеспечить возможность комбинировать оператор возвсдеция в степень с оператором присваивания так же, как для других арифметических операторов: например, ч и = дает е =. Это сразу исключает !, поскольку ) = уже имеет семантику. Мэтт Остерн предложил *" и, наверное, данное решение — лучшее из возможных.
Все остальные технические вопросы можно было уладить, ориентируясь на Рогггап, который является стандартом в этой области. Я снова всрнулся к ** для обозначения оператора возведсния в степень в С++. Да, я продемонстрировал, что с помощью традиционных методов невозможно использовать ** для указанной операции, но, обдумывая этот вопрос еще раз, вонял, что проблему совместимости с С можно обойти, применив определенный прием при проектировании компилятора.
Предположим, мы ввели оператор **. Несовместимость можно устранить, если определить его семантику как «разыменовать и умножитьь в случае, когда второй операнд является указателем: '-66ИИИИИИ Добавление в С++ операторов несущественные синтаксические вопросы, например следует ли записывать воз- ведение в степень в виде роэг(а, Ь) или а**Ь, или а* Ь. П.б.2.
Операторы, определяемые пользователем Удалось бы избежать споров об операторе возведения в степень, предоставив механизм, который позволил бы пользователям определять собственныс операторы? Тогда проблема отсутствия нужного оператора в общем виде была бы решена. Когда речь заходит об операторах, неизменно обнаруживается, что того набора, который предлагают С и С++, недостаточно для выражения всех необходимых операций. Обычное решение — определить функцию.
Однако когда для класса сеть возможность написать то запись вроде рон(а,Ь) аЬв(а) выглядит неудачно. Поэтому есть просьбы придать смысл таким выражениям, как а роы Ь аЬв а Это можно сделать. Как именно — показано в А!8о(88. Но дальше пользователи хотят, чтобы осмысленными были н такие строки: а // Ь (а Можно слелать и это. Вопрос только в том, действительно ли стоит разрешать пользователям определять операторы самим. В [АКМ! отмечалось: а = Ь**с**гп // (Ь**с)**к) или Ь**(с**г()7 бып бы неочевиден. Понадобилось бы также разрешать синтаксические конфликты с обычными операторами. В предположении, что *' и // определены кок бинарные операторы, россмот- рим токой пример: а = а**р; // а**р ИЛИ а*(*р) а = а//р; *р = 7; // а = а*р = 7; может быть Следовательно, определенные пользователем операторы должны либо состоять только из обычных символов, либо включать в себя какой-то легко отличимый префикс, например .
(точка): а роы Ь; // альтернатива 1 а .рон Ь; // альтернатива 2 а .** Ь; // альтернатива 3 Такое расширение, однако, резко усложнило бы синтаксический анализ и могло непредсказуемым образом сказаться но читаемости программы. Пришлось бы или разрешить пользователям задавать приоритет и ассоциативность новых операторов, или считать, что для всех новых операторов эти отрибуты фиксированы. В любом случае порядок вычисления вырожения видо Перегрузка БИИИИИИВ Определенным пользователем операторам слелует назначить приоритет.
Самый простой способ — считать, что у всех подобных операторов приоритет одинаков и совпадает с приоритетом какого-либо встроенного оператора. Однако этого недостаточно для «корректного» определения оператора возвсдения в степень. Например: оретасот ранг Ь1пату, ртесеаепсе Ьесиееп * апа цпату Также я опасаюсь, что программы, содержащие определенные пользователем операторы, для которых он же задал приоритеты, будет невозможно читать. Например, в языках программирования приоритет операции возведения в степень определяется по-разному. Поэтому пользователи станут неодинаково определять приоритет оператора ром.
Значит, синтаксический анализатор будет по-разному разбирать выражение а = — Ь роы с * б; в разных программах. Проще присвоить всем определенным пользователем операторам один и тот же приоритет. Это решение казалось заманчивым, пока не выяснилось, что даже со своими ближайшими сотрудниками — Эндрю Кенигом и Джонатаном Шапиро — мы не можем прийти к общему мнению относительно того, каким должен быть этот приоритет.
Очевидные варианты — «очень высокий» (скажем, выше, чем у умножения) и «очень низкий» (например, ниже, чем у присваивания). Увы, число случаев, когда один выбор представляется идеальным, другой — абсурдным, не поддается исчислению. Так, даже простейшие примеры не удается написать «правильно», если есть только один уровень приоритета. Убедитесь сами: а = Ь * с рон г(г а = Ь ртог)цсс с рои б; а рцС Ь + сг Поэтому в С++ и нет определяемых пользователем операторов. 11.б.З. Составные операторы С++ поддерживает перегрузку унарных и бинарных операторов.
Видимо, стоило бы поддержать и перегрузку составных операторов. В АКМ данная идея объяснялась следующим образом: Например, два умнохения в примере МаСт1х а, Ь, с, с(г // а = Ь * с * аг можно было бы реализовать специальным оператором «двойного умноягения», определенным так масттх оретасот * * (маст1ха, маст1хь, мастсхь); и тогда предлохение интерпретировалось бы; а = оретасот * * (Ь,с,с))г 1!ИИИИИИИ) Перечисления Иначе говоря, обнаружив один раз объявление Ф Маггхх орегагог * * (Масг1хй, Магх1хй, масгтхй); Магххх орегагох = * Иасг1хй, сопвс Иасг1хй, ооцЫе, сопвс иасх1хй ): можно было бы использовать для обработки предложений типа: а=Ь*1.7+аг Естественно, пробел в таких объявлениях очень важен. Впрочем, вместо него для обозначения позиций операндов можно было использовать и любую другую лексему: Масхтх орегасог.=.*.г.г масгтхй, сопвс масхтхй, аоцЫе, сопвс масгьхй ): До выхода АгсМ я никогда не видел изложения этого решения в печати, но такая техника часто применяется в кодогенераторах.
Данная идея представляется многообещающей для поддержки оптимизированных операций с векторами и матрицами, но уменя так и не нашлось времени продумать ее до конца. В существующей нотации это было бы неплохой поддержкой старого приема, заключающегося в определении функций для выполнения составных операций нзд переданными аргументами.
11.7. Перечисления В С концепция перечислений выглядит незаконченной. В первоначальном варианте языка их не было. Перечисления ввели без всякой охоты в качестве уступки тем, кто настойчиво требовал более основательных символических констант, чем препроцессорные макросы без параметров. Поэтому в С значение перечислителя имеет тип 1пс, равно как и значение переменной, объявленной как имеющая тип перечисления. Значение типа хпС можно присваивать переменной типа перечисления. Например: епцяг Со1ох 1 хес), дгееп, Ыце го1с1 1() /* функция С *! компилятор начнет искать повторяющиеся умножения масгтх и вызывать для ик интерпретации функцию. Слишком сложные для интерпретации последовательности будут обрабатываться обычными (упорными и бинарными) операторами.
Такое расширение предлагалось несколько раз как эффективный способ обработки повторяющихся вычислений в научных расчетах, где используются определенные пользователем типы. Например, оператор ИИИИИИИИВ Перегрузка ( епиш Со1ог с = 2; /* правильно */ /* правлльно */ 1пс 1 = с; епиш Ченьс1е ( саг, )зогве Ьидду, госкес чо1б д(рс,рч) епиш Со1ог* рс; епиш Чеп(с1е* рч; ( рс = рч/ ) /* возможно, некорректно в ХНБ1 С */ Я долго обсуждал возникшую проблему с такими экспертами по С, как Дэвид Хансов (Рач(д Напвоп), Брайан Керниган, Эндрю Кениг, Дуг Макилрой, Дэвид Проссер и Деннис Ричи. Мы так и не смогли прийти к окончательному выводу— что само по себе было недобрым знаком, — но регцнли, что в комитете собирались объявить этот пример незаконным.