Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - Скотт Мейерс на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

Эффективный и современный С++

42 рекомендации по использованию С++11 и С++14

Скотт Мейерс

Дарле,

выдающемуся черному лабрадору-ретриверу

Отзывы о книге “Эффективный и современный С++

Вас интересует С++? Современный С++ (т.е. C++11/C++14) — это гораздо больше чем простое внесение косметических изменений в старый стандарт. Учитывая новые возможности языка, это скорее его переосмысление. Вам нужна помощь в его освоении? Тогда перед вами именно та книга которая вам нужна. Что касается С++, то Скотт Мейерс был и остается синонимом точности, качества и удовольствия от чтения.

— Герхард Крейцер (Gerhard Kreuzer) Инженер-исследователь в Siemens AG

Трудно получить достаточный опыт и стать экспертом. Не менее трудно стать настоящим учителем, способным просто и ясно донести сложный материал до ученика. Если вы читаете эту книгу, то вы знаете человека, который объединяет оба эти качества. Книга Эффективный и современный С++ написана непревзойденным техническим писателем, который умеет излагать сложные взаимосвязанные темы ясно и понятно, блестящим литературным стилем. При этом вряд ли вам удастся найти в книге хотя бы одну техническую ошибку.

— Андрей Александреску (Andrei Alexandrescu) Доктор философии, исследователь, автор книги Современное проектирование на С++

Когда человек с более чем двадцатилетним опытом работы с С++ берется рассказать, как получить максимальную отдачу от современного С++ (рассказывая как о лучших подходах, так и о возможных ловушках, которых следует избегать), я настоятельно рекомендую внимательно прочесть его книгу! Я, определенно, узнал из нее много нового!

— Невин Либер (Nevin Liber) Старший программист в DRW Trading Group

Бьярне Страуструп — создатель С++ — сказал: “С++11 выглядит как новый язык программирования”: Книга Эффективный и современный С++ заставляет нас разделить это впечатление, поясняя, как использовать новые возможности и идиомы С++11 и С++14 в повседневной практике. Это еще одна талантливая книга Скотта Мейерса.

— Кассио Нери (Cassio Neri) Аналитик в Lloyds Banking Group

Скотт умеет добраться до самой сути любой технической проблемы. Книги серии Эффективный С++ способствовали улучшению стиля кодирования предыдущего поколения программистов С++; новая книга делает то же самое с программистами на современном С++.

— Роджер Орр (Roger Orr) OR/2 Limited, член Комитета ISO по стандартизации С++

Эффективный и современный С++ — отличный инструмент для повышения вашего уровня как программиста на современном С++. Книга не только учит тому, как, когда и где эффективно использовать современный С++, но и почему делать это именно так. Вне всякого сомнения, эта книга Скотта Мейерса даст программистам гораздо лучшее понимание языка.

— Барт Вандвустин (Bart Vandewoestyne) Инженер, исследователь и просто энтузиаст С++

Я люблю С++, он десятилетиями был моей рабочей лошадкой. А с новыми копытами эта лошадка стала еще сильнее и привлекательнее, чем я мог ранее себе представить. Но при больших изменениях всегда встает вопрос “Когда и как пользоваться всем этим богатством?” Как всегда, книга Скотта Мейерса компетентно и исчерпывающе отвечает на поставленный вопрос.

— Дамьен Уоткинс (Dаmiеn Watkins) Руководитель группы программной инженерии в CSIRO

Отличное чтение для перехода к современному С++ — новинки языка C++11/14 описаны наряду с C++98, разделение содержимого книги на разделы позволяет легко найти интересующую тему, а в конце каждого раздела приведены итоговые рекомендации. Книга интересна и полезна для программистов на С++ всех уровней.

— Рейчел Ченг (Rachel Cheng) F5 Networks

Если вы переходите с С++98/03 на C++11/14, вам нужна точная практичная информация, которую вам предоставляет Скотт Мейерс в книге Эффективный и современный С++. Если вы уже пишете код на С++11, то, вероятно, сталкивались с проблемами при использовании новых возможностей, которые легко решаются с помощью книги Скотта. В любом случае можно уверенно утверждать, что время, затраченное на чтение этой книги, не пропадет впустую.

— Роб Стюарт (Rob Stewart) Член Boost Steering Committee (boost.org)

Об авторе

Скотт Мейерс — один из ведущих мировых экспертов по С++, широко востребованный как инструктор, консультант и докладчик на разных конференциях. Более чем 20 лет книги Скотта Мейерса серии Эффективный С++ являются критерием уровня книг по программированию на С++. Скотт Мейерс имеет степень доктора философии (Ph.D.) в области компьютерных наук в Университете Брауна (Brown University). Его сайт находится по адресу aristeia.com.

Об изображении на обложке

На обложке книги Эффективный и современный С++ изображен розовошапочный пестрый голубь (Ptilinopus regina). Еще одно имя этого вида голубя — Свенсонов пестрый голубь (Swainson's fruit dove). Он отличается ярким оперением: серые голова и грудь, оранжевый живот, беловатое горло, желто-оранжевый цвет радужки и серо-зеленые ноги.

Голубь распространен в равнинных лесах восточной Австралии, муссонных лесах северной части Австралии, а также на малых Зондских островах и Молуккских островах Индонезии. Рацион голубя состоит из различных фруктов наподобие инжира (который он поедает целиком), пальм и лоз. Еще одним источником пищи голубя является камфорный лавр, большое вечнозеленое дерево. Они питаются парами, небольшими стайками или поодиночке в тропических лесах, обычно утром или поздно вечером. Для питья они используют воду из листьев или росу, но не воду с земли.

Пестрый голубь в Новом Южном Уэльсе считается видом, находящимся под угрозой исчезновения из-за изменения среды обитания — лесозаготовок, очистки и улучшения земель, а также вырубки камфорного лавра без адекватной альтернативы.

Многие из животных, представленных на обложках O'Reilly, находятся под угрозой исчезновения. Все они имеют очень важное значение для нашего мира. Чтобы узнать больше о том, как вы можете помочь им, посетите сайт animals.oreilly.com.

Изображение для обложки взято из Иллюстрированной истории природы Вуда (Wood), из тома, посвященного птицам.

Благодарности

Я начал исследовать то, что тогда было известно как C++0x (зарождающийся С++11), в 2009 году. Я опубликовал множество вопросов в группе новостей Usenet comp.std.c++ и благодарен членам этого сообщества (в особенности Дэниелу Крюглеру (Daniel Krugler)) за их очень полезные сообщения. В последние годы с вопросами по С++11 и С++14 я обращался к Stack Overflow, и я многим обязан этому сообществу за его помощь в понимании тонкостей современного С++.

В 2010 году я подготовил материалы для учебного курса по С++0x (в конечном итоге опубликованные как Overview of the New С++, Artima Publishing, 2010). На эти материалы, как и на мои знания, большое влияние оказала техническая проверка, выполненная Стивеном Т. Лававеем (Stephan T. Lavavej), Бернгардом Мерклем (Bernhard Merkle), Стенли Фризеном (Stanley Friesen), Леором Зорманом (Leor Zolman), Хендриком Шобером (Hendrik Schober) и Энтони Вильямсом (Anthony Williams). Без их помощи я не смог бы довести мою книгу до конца. Кстати, ее название было предложено несколькими читателями в ответ на мое сообщение в блоге от 18 февраля 2014 года (“Помогите мне назвать мою книгу”), и Андрей Александреску (Andrei Alexandrescu) (автор книги Modern С++ Design[1], Addison-Wesley, 2001) был достаточно великодушен, чтобы не счесть это название незаконным вторжением на его территорию.

Я не могу указать источники всей информации в этой книге, но некоторые из них непосредственно повлияли на мою книгу. Применение в разделе 1.4 неопределенного шаблона для получения информации о типе от компилятора было предложено Стивеном T. Лававеем, а Мэтт П. Дзюбински (Matt P. Dziubinski) обратил мое внимание на Boost.TypeIndex. В разделе 2.1 пример unsigned std::vector<int>::size_type взят из статьи Андрея Карпова (Andrey Karpov) от 28 февраля 2010 года “In what way can С++0x standard help you eliminate 64-bit errors”. Пример std::pair<std::string, int>/std::pair<const std::string, int> в том же разделе книги взят из сообщения “STL11: Magic && Secrets” Стивена T. Лававея на Going Native 2012. Раздел 2.2 появился благодаря статье Герба Саттера (Herb Sutter) “GotW #94 Solution: AAA Style (Almost Always Auto)” от 12 августа 2013 года, а раздел 3.3 — благодаря сообщению в блоге Мартинго Фернандеса (Martinho Fernandes) от 27 мая 2012 года — “Handling dependent names”. Пример в разделе 3.6 демонстрирует перегрузку квалификаторов ссылок, основанную на ответе Кейси (Casey) на вопрос “What's а use case for overloading member Junctions on reference qualifiers?” в сообществе Stack Overflow от 14 января 2014 года. В разделе 3.9 описание расширенной поддержки constexpr-функций в С++14 включает информацию, которую я получил от Рейна Халберсма (Rein Halbersma). Раздел 3.10 основан на презентации Герба Саттера на конференции С++ and Beyond 2012 под названием “You don't know const и mutable”. Совет в разделе 4.1, гласящий, что фабричная функция должна возвращать std::unique_ptr, основан на статье Герба Саттера “GotW# 90 Solution: Factories” от 13 мая 2013 года. fastLoadWidget в разделе 4.2 получен из презентации Герба Саттера “My Favorite С++ 10-Liner” на конференции Going Native 2013. В моем описании std::unique_ptr и неполных типов в разделе 4.5 использованы статья Герба Саттера от 27 ноября 2011 года “GotW #100: Compilation Firewalls”, а также ответ Говарда Хиннанта (Howard Himant) от 22 мая 2011 года на вопрос в Stack Overflow “Is std::unique_ptr<T> required to know the full definition of T?” Дополнительный пример Matrix в разделе 5.3 основан на письме Дэвида Абрахамса (David Abrahams). Комментарий Джо Аргонна (Joe Argonne) от 8 декабря 2012 года к материалу из блога “Another alternative to lambda move capture” от 30 ноября 2013 года стал источником для описанного в разделе 6.2 подхода к имитации инициализации на основе std::bind в C++11. Пояснения в разделе 7.3 проблемы с неявным отключением в деструкторе std::thread взяты из статьи Ганса Бехма (Hans-J. Boehm) “N2802: А plea to reconsider detach-on-destruction for thread objects” от 4 декабря 2008 года. Раздел 8.1 появился благодаря обсуждению материала в блоге Дэвида Абрахамса “Want speed? Pass by value” от 15 августа 2009 года. Идея о том, что типы, предназначенные только для перемещения, должны рассматриваться отдельно, взята у Мэттью Фьораванте (Matthew Fioravante), в то время как анализ копирования на основе присваивания взят из комментариев Говарда Хиннанта (Howard Hinnant). В разделе 8.2 Стивен T. Лававей и Говард Хиннант помогли мне понять вопросы, связанные с относительной производительностью функций размещения и вставки, а Майкл Винтерберг (Michael Winterberg) привлек мое внимание к тому, как размещение может приводить к утечке ресурсов. (Майкл, в свою очередь, называет своим источником презентацию “С++ Seasoning” Шона Парента (Sean Parent) на конференции GoingNative 2013. Майкл также указал, что функции размещения используют непосредственную инициализацию, в то время как функции вставки используют копирующую инициализацию.)

Проверка черновиков технической книги является длительной и критичной, но совершенно необходимой работой, и мне повезло, что так много людей были готовы за нее взяться. Черновики этой книги были официально просмотрены такими специалистами, как Кассио Нери (Cassio Neri), Нейт Кёль (Nate Kohl), Герхард Крейцер (Gerhard Kreuzer), Леон Золман (Leor Zolman), Барт Вандевойстин (Bart Vandewoestyne), Стивен T. Лававей (Stephan T. Lavavej), Невин Либер (Nevin “:-)” Liber), Речел Ченг (Rachel Cheng), Роб Стюарт (Rob Stewart), Боб Стигалл (Bob Steagall), Дамьен Уоткинс (Damien Watkins), Брэдли Нидхам (Bradley E. Needham), Рейнер Гримм (Rainer Grimm), Фредрик Винклер (Fredrik Winkler), Джонатан Уокели (Jonathan Wakely), Герб Саттер (Herb Sutter), Андрей Александреску (Andrei Alexandrescu), Эрик Ниблер (Eric Niebler), Томас Беккер (Thomas Becker), Роджер Орр (Roger Orr), Энтони Вильямc (Anthony Williams), Майкл Винтерберг (Michael Winterberg), Бенджамин Хахли (Benjamin Huchley), Том Кирби-Грин (Tom Kirby-Green), Алексей Никитин (Alexey А. Nikitin), Вильям Дилтрай (Willie Dealtry), Хуберт Мэттьюс (Hubert Matthews) и Томаш Каминьски (Tomasz Kaminski). Я также получил отзывы ряда читателей с помощью сервисов O'Reilly's Early Release EBooks и Safari Books Online's Rough Cuts, посредством комментариев в моем блоге (The View from Aristeia) и электронной почтой. Я благодарен каждому, кто высказал свои замечания. Эта книга получилась гораздо лучше, чем она была бы без этой помощи. В особенности я признателен Стивену Т. Лававею и Робу Стюарту, чьи чрезвычайно подробные и всеобъемлющие замечания заставили меня забеспокоиться: кто из нас потратил больше сил и времени на эту книгу — я или они? Моя особая благодарность — Леору Золману (Leor Zolman), который не только просмотрел рукопись, но и дважды проверил все приведенные в ней примеры кода.

Черновики цифровых версий книги были подготовлены Герхардом Крейцером (Gerhard Kreuzer), Эмиром Вильямсом (Emyr Williams) и Брэдли Нидхэмом (Bradley E. Needham).

Мое решение ограничить длину строки кода 64 символами (максимум для правильного отображения на печати, а также на различных цифровых устройствах при разной ориентации и конфигурации шрифтов) было основано на данных, предоставленных Майклом Махером (Michael Maher).

С момента первой публикации я исправил ряд ошибок и внес некоторые усовершенствования, предложенные такими читателями, как Костас Влахавас (Kostas Vlahavas), Даниэль Алонсо Алеман (Daniel Alonso Alemany), Такатоши Кондо (Takatoshi Kondo), Бартек Сургот (Bartek Szurgot), Тайлер Брок (Tyler Brock), Джай Ципник (Jay Zipnick), Барри Ревзин (Вагу Revzin), Роберт Маккейб (Robert McCabe), Оливер Брунс (Oliver Bruns), Фабрис Ферино (Fabrice Ferino), Дэнез Джонитис (Dainis Jonitis), Петр Валашек (Petr Valasek) и Барт Вандевойстин (Bart Vandewoestyne). Большое спасибо всем им за помощь в повышении точности и ясности изложенного материала.

Эшли Морган Вильямc (Ashley Morgan Williams) готовила отличные обеды у себя в Lake Oswego Pizzicato. Им (и ей) нет равных.

И более двадцати лет моя жена, Нэнси Л. Урбано (Nancy L. Urbano), как обычно во время моей работы над новой книгой, терпит мою раздражительность и оказывает мне всемерную поддержку. В ходе написания книги постоянным напоминанием о том, что за пределами клавиатуры есть другая жизнь, служила мне наша собака Дарла.

Введение

Если вы — опытный программист на языке программирования С++, как, например, я, то, наверное, первое, о чем вы подумали в связи с С++11, — “Да, да, вот и он — тот же С++, только немного улучшенный”. Но познакомившись с ним поближе, вы, скорее всего, были удивлены количеством изменений. Объявления auto, циклы for для диапазонов, лямбда-выражения и rvalue-ссылки изменили лицо С++, — и это не говоря о новых возможностях параллельности. Произошли и идиоматические изменения. 0 и typedef уступили место nullptr и объявлениям псевдонимов. Перечисления получили области видимости. Интеллектуальные указатели стали предпочтительнее встроенных; перемещение объектов обычно предпочтительнее их копирования.

Даже без упоминания С++14 в С++11 есть что поизучать.

Что еще более важно, нужно очень многое изучить, чтобы использовать новые возможности эффективно. Если вам нужна базовая информация о “современных” возможностях С++, то ее можно найти в избытке. Но если вы ищете руководство о том, как использовать эти возможности для создания правильного, эффективного, сопровождаемого и переносимого программного обеспечения, поиск становится более сложным. Вот здесь вам и пригодится данная книга. Она посвящена не описанию возможностей С++ 11 и C++14, а их эффективному применению.

Информация в книге разбита на отдельные разделы, посвященные тем или иным рекомендациям. Вы хотите разобраться в разных видах вывода типов? Или хотите узнать, когда следует (а когда нет) использовать объявление auto? Вас интересует, почему функция-член, объявленная как const, должна быть безопасна с точки зрения потоков, как реализовать идиому Pimpl с использованием std::unique_ptr, почему следует избегать режима захвата по умолчанию в лямбда-выражениях или в чем различие между std::atomic и volatile? Ответы на эти вопросы вы найдете в книге. Более того, эти ответы не зависят от платформы и соответствуют стандарту. Это книга о переносимом С++.

Разделы книги представляют собой рекомендации, а не жесткие правила, поскольку рекомендации имеют исключения. Наиболее важной частью каждого раздела является не предлагаемая в нем рекомендация, а ее обоснование. Прочитав раздел, вы сможете сами определить, оправдывают ли обстоятельства вашего конкретного проекта отход от данной рекомендации. Истинная цель книги не в том, чтобы рассказать вам, как надо поступать или как поступать не надо, а в том, чтобы обеспечить вас более глубоким пониманием, как та или иная концепция работает в С++11 и С++14.

Терминология и соглашения

Чтобы мы правильно понимали друг друга, важно согласовать используемую терминологию, начиная, как ни странно это звучит, с термина “С++”. Есть четыре официальные версии С++, и каждая именуется с использованием года принятия соответствующего стандарта ISO: С++98, C++03, C++11 и С++14. С++98 и C++03 отличаются один от другого только техническими деталями, так что в этой книге обе версии я называю как С++98. Говоря о С++11, я подразумеваю и С++11, и С++14, поскольку С++ 14 является надмножеством С++11. Когда я пишу “С++14”, я имею в виду конкретно С++14. А если я просто упоминаю С++, я делаю утверждение, которое относится ко всем версиям языка.

Использованный термин Подразумеваемая версия
С++ Все
С++98 С++98 и С++03
С++11 С++11 и С++14
С++14 С++14

В результате я мог бы сказать, что в С++ придается большое значение эффективности (справедливо для всех версий), в С++98 отсутствует поддержка параллелизма (справедливо только для С++98 и С++03), С++11 поддерживает лямбда-выражения (справедливо для C++11 и С++14) и С++14 предлагает обобщенный вывод возвращаемого типа функции (справедливо только для С++14).

Наиболее важной особенностью С++11, вероятно, является семантика перемещения, а основой семантики перемещения является отличие rvalue-выражений от lvaluе-выражений. Поэтому rvalue указывают объекты, которые могут быть перемещены, в то время как lvalue в общем случае перемещены быть не могут. Концептуально (хотя и не всегда на практике), rvalue соответствуют временным объектам, возвращаемым из функций, в то время как lvalue соответствуют объектам, на которые вы можете ссылаться по имени, следуя указателю или lvalue-ссылке.

Полезной эвристикой для выяснения, является ли выражение lvalue, является ответ на вопрос, можно ли получить его адрес. Если можно, то обычно это lvalue. Если нет, это обычно rvalue. Приятной особенностью этой эвристики является то, что она помогает помнить, что тип выражения не зависит от того, является ли оно lvalue или rvalue. Иначе говоря, для данного типа Т можно иметь как lvalue типа Т, так и rvalue типа Т. Особенно важно помнить это, когда мы имеем дело с параметром rvalue ссылочного типа, поскольку сам по себе параметр является lvalue:

class Widget {

public:

 Widget(Widget&& rhs); // rhs является lvalue, хотя

                       // и имеет ссылочный тип rvalue

};

Здесь совершенно корректным является взятие адреса rhs в перемещающем конструкторе Widget, так что rhs представляет собой lvalue, несмотря на то что его тип — ссылка rvalue. (По сходным причинам все параметры являются lvalue.)

Этот фрагмент кода демонстрирует несколько соглашений, которым я обычно следую.

• Имя класса — Widget. Я использую слово Widget, когда хочу сослаться на произвольный пользовательский тип. Если только мне не надо показать конкретные детали класса, я использую имя Widget, не объявляя его.

• Я использую имя параметра rhs (“right-hand side”, правая сторона). Это предпочитаемое мною имя параметра для операций перемещения (например, перемещающего конструктора и оператора перемещающего присваивания) и операций копирования (например, копирующего конструктора и оператора копирующего присваивания). Я также использую его в качестве правого параметра бинарных операторов:

Matrix operator+(const Matrix& lhs, const Matrix& rhs);

Я надеюсь, для вас не станет сюрпризом, что lhs означает “left-hand side” (левая сторона).

• Я использую специальное форматирование для частей кода или частей комментариев, чтобы привлечь к ним ваше внимание. В перемещающем конструкторе Widget выше я подчеркнул объявление rhs и часть комментария, указывающего, что rhs представляет собой lvalue. Выделенный код сам по себе не является ни плохим, ни хорошим. Это просто код, на который вы должны обратить внимание.

• Я использую “”, чтобы указать “здесь находится прочий код”. Такое “узкое” троеточие отличается от широкого “...”, используемого в исходных текстах шаблонов с переменным количеством параметров в С++11. Это кажется запутанным, но на самом деле это не так. Вот пример.

template<typename... Ts>              // Эти троеточия

void processVals(const Ts&... params) // в исходном

{                                     // тексте С++

 …                                    // Это троеточие озна-

                                      // чает какой-то код

}

Объявление processVals показывает, что я использую ключевое слово typename при объявлении параметров типов в шаблонах, но это просто мое личное предпочтение; вместо него можно использовать ключевое слово class. В тех случаях, когда я показываю код, взятый из стандарта С++, я объявляю параметры типа с использованием ключевого слова class, поскольку так делает стандарт.

Когда объект инициализирован другим объектом того же типа, новый объект является копией инициализирующего объекта, даже если копия создается с помощью перемещающего конструктора. К сожалению, в С++ нет никакой терминологии, которая позволяла бы различать объекты, созданные с помощью копирующих и перемещающих конструкторов:

void someFunc(Widget w);  // Параметр w функции someFunc

                          // передается по значению

Widget wid;               // wid - объект класса Widget

someFunc(wid);            // В этом вызове someFunc w

                          // является копией wid, созданной

                          // копирующим конструктором

someFunc(std::move(wid)); // В этом вызове SomeFunc w

                          // является копией wid, созданной

                          // перемещающим конструктором

Копии rvalue в общем случае конструируются перемещением, в то время как копии lvalue обычно конструируются копированием. Следствием является то, что если вы знаете только то, что объект является копией другого объекта, то невозможно сказать, насколько дорогостоящим является создание копии. В приведенном выше коде, например, нет возможности сказать, насколько дорогостоящим является создание параметра w, без знания того, какое значение передано функции someFunc — rvalue или lvalue. (Вы также должны знать стоимости перемещения и копирования Widget.)

В вызове функции выражения, переданные в источнике вызова, являются аргументами функции. Эти аргументы используются для инициализации параметров функции. В первом вызове someFunc, показанном выше, аргументом является wid. Во втором вызове аргументом является std::move(wid). В обоих вызовах параметром является w. Разница между аргументами и параметрами важна, поскольку параметры являются lvalue, но аргументы, которыми они инициализируются, могут быть как rvalue, так и lvalue. Это особенно актуально во время прямой передачи, при которой аргумент, переданный функции, передается другой функции так, что при этом сохраняется его “правосторонность” или “левосторонность” (Прямая передача подробно рассматривается в разделе 5.8.)

Хорошо спроектированные функции безопасны с тачки зрения исключений, что означает, что они обеспечивают как минимум базовую гарантию, т.е. гарантируют, что, даже если будет сгенерировано исключение, инварианты программы останутся нетронутыми (т.е. не будут повреждены структуры данных) и не будет никаких утечек ресурсов. Функции, обеспечивающие строгую гарантию, гарантируют, что, даже если будет сгенерировано исключение, состояние программы останется тем же, что и до вызова функции.

Говоря о функциональном объекте, я обычно имею в виду объект типа, поддерживающего функцию-член operator(). Другими словами, это объект, действующий, как функция. Иногда я использую термин в несколько более общем смысле для обозначения чего угодно, что может быть вызвано с использованием синтаксиса вызова функции, не являющейся членом (т.е. functionName(arguments)). Это более широкое определение охватывает не только объекты, поддерживающие operator(), но и функции и указатели на функции в стиле С. (Более узкое определение происходит из С++98, более широкое — из C++11.) Дальнейшее обобщение путем добавления указателей на функции-члены дает то, что известно как вызываемый объект (callable object). Вообще говоря, можно игнорировать эти тонкие отличия и просто рассматривать функциональные и вызываемые объекты как сущности в С++, которые могут быть вызваны с помощью некоторой разновидности синтаксиса вызова функции.

Функциональные объекты, создаваемые с помощью лямбда-выражений, известны как замыкания (closures). Различать лямбда-выражения и замыкания, ими создаваемые, приходится редко, так что я зачастую говорю о них обоих как о лямбдах (lambda). Точно так же я редко различаю шаблоны функций (function templates) (т.е. шаблоны, которые генерируют функции) и шаблонные функции (template functions) (т.е. функции, сгенерированные из шаблонов функций). То же самое относится к шаблонам классов и шаблонным классам.

Многие сущности в С++ могут быть как объявлены, так и определены. Объявления вводят имена и типы, не детализируя информацию о них, такую как их местоположение в памяти или реализация:

extern int x;               // Объявление объекта

class Widget;               // Объявление класса

bool func(const Widget& w); // Объявление функции

enum class Color;           // Объявление перечисления

                            // с областью видимости

                            // (см. раздел 3.4)

Определение предоставляет информацию о расположении в памяти и деталях реализации:

int x;                    // Определение объекта

class Widget {            // Определение класса

};

bool func(const Widget& w)

 { return w.size() < 10; } // Определение функции



Поделиться книгой:

На главную
Назад