Совет 6. Остерегайтесь странностей лексического разбора С++
Совет 6. Остерегайтесь странностей лексического разбора С++
Предположим, у вас имеется файл, в который записаны числа типа int, и вы хотите скопировать эти числа в контейнер list. На первый взгляд следующее решение выглядит вполне разумно:
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // Внимание! Эта строка
istream_iterator<int>()); // работает не так, как
// вы предполагали
Идея проста: передать пару istream_iterator интервальному конструктору list (совет 5), после чего скопировать числа из файла в список.
Программа будет компилироваться, но во время выполнения она ничего не сделает. Она не прочитает данные из файла. Она даже не создаст список — а все потому, что вторая команда не объявляет список и не вызывает конструктор. Вместо этого она... Произойдет нечто настолько странное, что я даже не рискну прямо сказать об этом, потому что вы мне не поверите. Вместо этого я попробую объяснить суть дела постепенно, шаг за шагом. Надеюсь, вы сидите? Если нет — лучше поищите стул...
Начнем с азов. Следующая команда объявляет функцию f, которая получает double и возвращает int:
int f(double d);
То же самое происходит и в следующей строке. Круглые скобки вокруг имени параметра d не нужны, поэтому компилятор их игнорирует:
int f(double(d));// То же,- круглые скобки вокруг d игнорируются
Рассмотрим третий вариант объявления той же функции. В нем просто не указано имя параметра:
int f(double);// То же; имя параметра не указано
Вероятно, эти три формы объявления вам знакомы, хотя о возможности заключать имена параметров в скобки известно далеко не всем (до недавнего времени я о ней не знал).
Теперь рассмотрим еще три объявления функции. В первом объявляется функция g с параметром — указателем на функцию, которая вызывается без параметров и возвращает double:
int g(double (*pf)()); // Функции g передается указатель на функцию
То же самое можно сформулировать и иначе. Единственное различие заключается в том, что pf объявляется в синтаксисе без указателей (допустимом как в С, так и в С++):
int g(double pf()); // То же; pf неявно интерпретируется как указатель
Как обычно, имена параметров могут опускаться, поэтому возможен и третий вариант объявления g без указания имени pf:
int g(double());// То же: имя параметра не указано
Обратите внимание на различия между круглыми скобками вокруг имени параметра (например, параметра d во втором объявлении f) и стоящими отдельно (как в этом примере). Круглые скобки, в которые заключено имя параметра, игнорируются, а круглые скобки, стоящие отдельно, обозначают присутствие списка параметров; они сообщают о присутствии параметра, который является указателем на функцию.
После небольшой разминки с объявлениями f и g мы возвращаемся к фрагменту, с которого начинается этот совет. Ниже он приводится снова:
list<int> data(istream_iterator<int>(dataFile),
istream_iterator<int>());
Держитесь и постарайтесь не упасть. Перед вами объявление функции data, возвращающей тип list<int>. Функция data получает два параметра:
•Первый параметр, dataFile, относится к типу istream_iterator<int>. Лишние круглые скобки вокруг dataFile игнорируются.
•Второй параметр не имеет имени. Он относится к типу указателя на функцию, которая вызывается без параметров и возвращает istream_iterator<int>.
Любопытно, не правда ли? Однако такая интерпретация соответствует одному из основных правил С++: все, что может интерпретироваться как указатель на функцию, должно интерпретироваться именно так. Каждый программист с опытом работы на С++ встречался с теми или иными воплощениями этого правила. Сколько раз вы встречались с такой ошибкой:
class Widget{...};// Предполагается, что у Widget
// имеется конструктор по умолчанию
Widget w();// Какая неприятность...
Вместо объекта класса Widget с именем w в этом фрагменте объявляется функция w, которая вызывается без параметров и возвращает Widget. Умение распознавать подобные «ляпы» — признак хорошей квалификации программиста С++.
Все это по-своему интересно, однако мы нисколько не приблизились к поставленной цели: инициализировать объект list<int> содержимым файла. Зато теперь мы знаем, в чем заключается суть проблемы, и легко справимся с ней. Объявления формальных параметров не могут заключаться в круглые скобки, но никто не запрещает заключить в круглые скобки аргумент при вызове функции, поэтому простое добавление круглых скобок поможет компилятору увидеть происходящее под нужным углом зрения:
list<int> data((istream_iterator<int>(dataFile)), // Обратите внимание istream_iterator<int>()); // на круглые скобки
// вокруг первого аргумента
// конструктора list
Именно так следует объявлять данные. Учитывая практическую полезность istream_iterator и интервальных конструкторов (совет 5), этот прием стоит запомнить.
К сожалению, не все компиляторы знают об этом. Из нескольких протестированных компиляторов почти половина соглашалась только на неправильное объявление data без дополнительных круглых скобок! Чтобы умиротворить такие компиляторы, можно закатить глаза и воспользоваться неверным, как было показано выше, объявлением data, но это недальновидное и плохо переносимое решение.
Более грамотный выход заключается в том, чтобы отказаться от модного использования анонимных объектов istream_iterator при объявлении data и просто присвоить этим итераторам имена. Следующий фрагмент работает всегда:
ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin.dataEnd);
Именованные объекты итераторов противоречат стандартному стилю программирования STL, но зато ваша программа будет однозначно восприниматься как компиляторами, так и людьми, которые с ними работают.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
3.2.3.2. Анализ лексического состава текста
3.2.3.2. Анализ лексического состава текста Программа ЛЕКС1Программа подсчитывает, сколько раз в тексте (области) употребляется то или иное слово. Программа формирует полный список всех различных слов текста с указанием частот их встречаемости. Можно задать диапазон частот
Пересмотр лексического анализа
Пересмотр лексического анализа Введение У меня есть хорошие и плохие новости. Плохие новости – эта глава не та, которую я вам обещал последний раз. Более того, и следующая глава также.Хорошие новости в причине появления этой главы: я нашел способ упростить и
Совет 2. Остерегайтесь иллюзий контейнерно-независимого кода
Совет 2. Остерегайтесь иллюзий контейнерно-независимого кода Основным принципом STL является обобщение. Массивы обобщаются в контейнеры, параметризованные по типам хранящихся объектов. Функции обобщаются в алгоритмы, параметризованные по типам используемых итераторов.
СОВЕТ
СОВЕТ Вы должны изучать программирование активно, а не просто пассивно читать данную книгу. С этой целью мы включили в нее много примеров. Вы должны попытаться решить, хотя бы некоторые из них на вашей вычислительной системе, чтобы получить луч шее представление о том, как
Остерегайтесь монстра разбухания
Остерегайтесь монстра разбухания Более зрелый — не значит более сложныйС развитием событий не бойтесь противостоять разбуханию. Всегда будет соблазн расширять продукт в объеме. Но это делать не обязательно. То, что продукт растет и становится более зрелым — не должно
Совет 6 Не слушай родителей
Совет 6 Не слушай родителей В нашей культуре следование родительским советам считается чем-то сакральным. Это обязанность ребенка, почти такая же незыблемая, как религиозные догматы. Книжные, киношные и телевизионные сюжеты завязаны на родительскую мудрость. Но в нашей
Совет 31 Не паникуй
Совет 31 Не паникуй Карьеру программиста я начал из-за видеоигр. Интерактивные приключения с эффектом присутствия очаровали меня еще во времена Commodore 64 с его видеолентами. Я стеснялся своего пристрастия, но теперь понимаю, что нечего было стыдиться. Компьютерные игры
Совет 36 Будь рядом
Совет 36 Будь рядом Если у тебя есть возможность лично пообщаться с руководством и коммерческими заказчиками. Не упускай ее.В Бангалоре, когда я занимал должность главного технического директора, у меня был неприятный опыт докладов начальнице, которую я не любил (она
Совет 38 Меняй мир
Совет 38 Меняй мир Худшее, что о тебе могут спросить на работе «А чем он (или она) вообще занимается?» Такая постановка вопроса подразумевает, что человек понятия не имеет о достигнутых тобой результатах.Как ни грустно признаваться, но я понятия не имею о достижениях
Совет 42 Незаурядность
Совет 42 Незаурядность Традиционный комплекс маркетинга стоит на четырех p: product (продукт), price (цена), promotion (продвижение) и placement (распространение). Существует мнение, что, охватив все четыре категории, ты получишь полный план сбыта продукции. При этом всем категориям нужно
Совет 44 Ты уже устарел
Совет 44 Ты уже устарел Многих из нас сфера информационных технологий привлекает тем, что там постоянно появляется что-то новое. Захватывающая и свежая рабочая атмосфера. Всегда есть возможность научиться чему-то новому. И в то же время приводящая в уныние ситуация, когда
Совет 45 Ты уже безработный
Совет 45 Ты уже безработный Работы, на которую тебя наняли, больше не существует. Возможно, ты все еще получаешь зарплату. Может быть, ты способствуешь росту прибыльности предприятия. Допускаю даже, что ты приводишь в полный восторг своего работодателя. Но свою работу ты
Остерегайтесь полиморфных кэтколлов!
Остерегайтесь полиморфных кэтколлов! Правило Системной Корректности пессимистично: в целях упрощения оно отвергает и вполне безопасные комбинации инструкций. Как ни парадоксально, но последний вариант решения мы построим на основе еще более пессимистического правила.
Microsoft Security Essentials: остерегайтесь подделок Андрей Крупин
Microsoft Security Essentials: остерегайтесь подделок Андрей Крупин Как и предсказывали специалисты "Лаборатории Касперского", в Интернете началась новая волна распространения фальшивых антивирусов. Изобретательные и циничные злоумышленники посредством спам-рассылок и рекламной