
Динамо-машины Метод Сократа
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 [ 69 ] 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
Задача 33. Ооооператоры Сложность: 4
Сколько операторов можно поместить подряд так, чтобы это имело смысл ? В определенном смысле эту задачу можно считать шуткой.
Вопрос для новичка
1. Какое наибольшее количество плюсов (+) могут быть расположены подряд, без включения пробельных символов, в корректной программе на С++? Примечание: конечно, плюсы в комментариях, директивах препроцессора, макросах и литералах не учитываются. Это было бы неспортивно.
Вопрос для профессионала
2. Аналогично, чему равно наибольшее количество каждых из приведенных далее символов, которые могут располагаться в программе подряд, без вставки пробельных символов, вне комментариев в корректной программе на С++?
а) amp;
б) lt;
в) I
Например, для пункта а) код if(a amp; amp;b) демонстрирует тривиальный случай двух п осл с до вате л ь н ы х символов amp; в корректной программе на С++. Попробуйте найти примеры с большим количеством символов.
Решение
Правило максимального глотка
Правило максимального глотка гласит, что при разбивке символов исходного текста на лексемы компилятор должен поступать в соответствии с жадным алгоритмом -- т.е. выбирать наиболее длинную из возможных лексем. Таким образом, raquo; всегда интерпретируется как единая лексема - оператор извлечения из потока (или битового сдвига вправо), и никогда - как две отдельные лексемы gt;, даже в ситуации наподобие следующей:
tempi ate lt;cl ass т = x lt;Y raquo;
Вот почему такой код следует писать с дополнительным пробелом:
tempiate lt;class Т = X lt;Y gt; gt;
Аналогично, raquo; gt; всегда интерпретируется как raquo; с последующим за ним gt;, но не как gt; с последующим raquo;.
Операторные шутки
1. Какое наибольшее количество плюсов (+) могут быть расположены подряд, без включения пробельных си.мволов, в корректной программе на С++?
Примечание: конечно, плюсы в комментариях, директивах препроцессора, макросах и литералах не учитываются. Это было бы неспортивно.
Можно создать исходный файл, содержащий последовательность символов + произвольной длины (ограниченной возможностями компилятора по обработке длинной строки).
Если последовательность состоит из четного количества символов +. она интерпретируется как ++ ++++++ ... ++, т.е. как последовательность двухсимвольных
лексем ++. Для того чтобы такой код был работоспособен и имел однозначно определенную семантику, все, что нам надо, - это класс, в котором определен пользовательский префиксный оператор ++, обеспечивающий возможность цепочечного вызова. Например:
пример 33-1(а)
class А { public:
А amp; operator++C) { return *this; }
Теперь мы можем записать код А а;
++++++а; Означает: ++ ++ ++ а;
который работает следующим образом
a.operator++().operator++().operator++C)
А что, если п осл едоватсл ьн ость имеет нечетное число символов +? Тогда она интерпретируется как ++ ++++++ ... ++ +, последовательность двухсимвольных лексем ++, за которой следует заключающий символ +. Для того чтобы такой код был работоспособен, надо дополнительно определить унарный оператор +.
пример 33-1(6)
class А { public:
А amp; operator+ () { return *this; } A amp; operator++0 { return *this; }
Теперь мы можем записать код А а;
+++++++а; означает: +++++++ а; который работает следующим образом.
a.operator+O.operator++().operator++().operator++()
Это очень простой трюк. Создание необычно длинных последовательностей других символов может оказаться немного сложнее, но все равно возможным.
Злоупотребление операторами
Код в примерах 33-1а и 33-16 не слишком злоупотребляет обычной семантикой операторов ++ и +. В дальнейшей работе, однако, нам придется далеко отойти от общепринятых правил кодирования. То, что вы увидите дальше, - не для промышленного использования, а всего лишь для собственного удовольствия.
2. Аналогично, чему равно наибольшее количество каждых из приведенных далее символов, которые могут располагаться в программе подряд, без вставки пробельных символов, вне комментариев в корректной программе на С++?
Для ответа на этот вопрос мы создадим вспомогательный класс Пример 33-2
class А { public:
void operator amp; amp;( int ) { }
void operator laquo;( int ) { }
void operator I( i nt ) { }
typedef void (A::*F)(int);
Теперь начнем с рассмотрения символа
а) amp;
Ответ: пять.
Ну, amp; amp; - это просто; не сложно и amp; amp; amp;. Так что пойдем сразу дальше. Можем ли мы создать последовательность из четырех символов amp; amp; amp; amp;? Если да, то она должна интерпретироваться как amp; amp; amp; amp;, но выражение наподобие а amp; amp; amp; amp; b синтаксически некорректно; мы не можем разместить два бинарных оператора один за другим.
Трюк заключается в том, что мы можем использовать вторую пару amp; amp; как оператор, сделав первую пару amp; amp; окончанием чего-то, что оператором ие является. В таком случае требуется не так много времени, чтобы увидеть, что первая пара amp; amp; может быть окончанием имени, а именно - имени функции, так что все, что нам нужно, - оператор operator amp; amp;(), который может получать указатель на некоторый другой оператор operateг amp; amp;() в качестве первого параметра:
void operator amp; amp;C F, А ) { } Это позволяет нам записать
amp;А::operateг amp; amp; amp; amp;а; amp; amp; amp; amp; что означает
operator amp; amp;( amp;А::operateг amp; amp;, а );
Это самая длинная последовательность из четного количества символов amp;, поскольку amp; amp; amp; amp; amp; amp; - некорректная запись. Почему? Потому что она означает amp; amp; amp; amp; amp; amp;, но даже делая первую пару amp; amp; частью имени, мы не можем сделать последнюю пару amp; amp; началом другого имени; остается только разместить два бинарных оператора amp; amp; подряд, что невозможно.
Но не можем ли мы добавить еще один символ amp;, чгобы получить нечетное количество символов amp; в последовательности? Конечно, можем. Конкретно - amp; amp; amp; amp; amp; означает amp; amp; amp; amp; amp;; для первой части решение у нас уже есть, так что не надо долго думать, чтобы суметь прицепить к нему унарный оператор amp;.
amp;А::operateг amp; amp; amp; amp; amp;а; amp; amp; amp; amp; amp; что означает
operator amp; amp;( amp;А::ере rateг amp; amp;, amp;а ); Теперь разберемся с оставшимися символами:
а) lt;
б) I
В обоих случаях ответ один - четыре.
Имея решение вопроса 2а, не сложно расправиться и с оставшимися двумя. Чтобы получить последовательность из четырех символов, воспользуемся тем же трюком, что и ранее, и определим
void eperator laquo;( F, д ) { } void operator I( F, a ) { }
Это позволит нам написать
amp;А: : ope rate r laquo; laquo;a; laquo; laquo; amp;a::operator I!a; MM
что означает
operater laquo;( amp;A: :eperater laquo;, a ) ; operator!( amp;A::eperaterM, a );
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 [ 69 ] 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |