Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Название:Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2016
- Город:Москва
- ISBN:978-5-8459-2000-3
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 краткое содержание
В книге рассматриваются следующие темы. Освоение С++11 и С++14 — это больше, чем просто ознакомление с вводимыми этими стандартами возможностями (например, объявлениями типов
, семантикой перемещения, лямбда-выражениями или поддержкой многопоточности). Вопрос в том, как использовать их эффективно, чтобы создаваемые программы были корректны, эффективны и переносимы, а также чтобы их легко можно было сопровождать. Именно этим вопросам и посвящена данная книга, описывающая создание по-настоящему хорошего программного обеспечения с использованием C++11 и С++14 — т.е. с использованием современного С++.
■ Преимущества и недостатки инициализации с помощью фигурных скобок, спецификации
, прямой передачи и функций
интеллектуальных указателей
■ Связь между
,
, rvalue-ссылками и универсальными ссылками
■ Методы написания понятных, корректных,
лямбда-выражений
■ Чем
отличается от
, как они используются и как соотносятся с API параллельных вычислений С++
■ Какие из лучших методов “старого” программирования на С++ (т.е. С++98) должны быть пересмотрены при работе с современным С++
Более чем 20 лет книги
серии
являются критерием уровня книг по программированию на С++. Понятное пояснение сложного технического материала принесло ему всемирную известность. Он всегда самый желанный гость на международных конференциях, а его услуги консультанта широко востребованы во всем мире.
Скотт Мейерс Эффективный и современный С++, После изучения основ С++ я перешел к изучению того, как применять С++ в промышленном программировании, с помощью серии книг Скотта Мейерса Эффективный С++. Эффективный и современный С++ — наиболее важная из книг серии, предлагающая ключевые рекомендации, стили и идиомы, позволяющие эффективно использовать современный С++. Вы еще не купили эту книгу? Сделайте это прямо сейчас. Герб Саттер,
глава Комитета ISO по стандартизации С++, специалист в области архитектуры программного обеспечения на С++ в Microsoft
Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
typedef std::vector::iterator IterT;
std::vector::const_iterator ConstIterT;
std::vector values;
ConstlterT ci =
std::find( static_cast(values.begin() ),
static_cast(values.end() ),
1983);
values.insert( static_cast(ci), // Может не
1998); // компилироваться!
Конструкции typedef
применять, конечно, необязательно, но они облегчают написание приведений. (Если вы удивляетесь, почему я использую typedef
, а не следую собственному совету из раздела 3.3 и не применяю объявлений псевдонимов, то я напомню, что в этом примере демонстрируется код С++98, а объявления псевдонимов — новая возможность в С++11.)
Приведения в вызове std::find
присутствуют потому, что values
является неконстантным контейнером, а в С++98 нет простого способа получить константный итератор из неконстантного контейнера. Приведения не являются строго необходимыми, так как можно получить const_iterator
другими способами (например, вы можете связать values
с переменной, являющейся ссылкой на константный объект, а затем использовать ее в своем коде вместо values
), но к какому бы способу вы ни прибегли, процесс получения const_iterator
, указывающего на элементы неконстантного контейнера, включает определенное количество “кривизны”.
После того как вы получаете const_iterator
, ситуация ничуть не улучшается, поскольку в С++98 местоположения для вставки (и удаления) могут указывать только неконстантные итераторы iterator
. Итераторы const_iterator
для этого неприменимы. Вот почему в приведенном выше коде я выполняю приведение const_iterator
(который я с таким трудом получил из std::find
) в iterator
: передача const_iterator
в функцию insert
не будет компилироваться.
Честно говоря, показанный мной код может не скомпилироваться, поскольку не существует переносимого преобразования const_iterator
в iterator
, даже с помощью static_cast
. Может не сработать даже семантическая кувалда reinterpret_cast
. (Это не ограничение С++98. Это справедливо и для C++11. const_iterator
просто не преобразуется в iterator
— неважно, насколько простым кажется такое преобразование.) Есть несколько переносимых способов сгенерировать iterator
, который указывает на то же, на что и const_iterator
, но они не очевидны, не универсальны и не стоят того, чтобы рассматривать их в данной книге. Кроме того, я надеюсь, что теперь понятна моя позиция: const_iterator
причиняли так много неприятностей в С++98, что редко стоили того, чтобы о них беспокоиться и их использовать. По большому счету программисты всегда использовали const
не где это только возможно , а только там, где это практично , а в С++98 const_iterator
особо практичным не является.
Все изменилось с появлением C++11. Теперь const_iterator
легко получить и легко использовать. Функции-члены контейнера cbegin
и cend
возвращают const_iterator
даже для неконстантных контейнеров, а функции-члены STL, которые применяют итераторы для указания позиций (например, insert
и erase
), в действительности используют итераторы const_iterator
. Переделка кода С++98, который использовал iterator
, в код с const_iterator
в С++11 воистину тривиальна:
std::vector values; // Как и ранее
…
autoit =
std::find(values. cbegin(), // Используем cbegin
values. cend(), 1983); // и cend
values.insert(it, 1998);
Теперь код, использующий const_iterator
, стал действительно практичным!
Почти единственной ситуацией, в которой поддержка С++11 для const_iterator
оказывается недостаточной, является ситуация, когда вы хотите написать максимально обобщенный библиотечный код. Такой код принимает во внимание то, что некоторые контейнеры и контейнерообразные структуры данных предоставляют функции begin
и end
(а также cbegin
, cend
, rbegin
и т.д.) как функции, не являющиеся членами. Это происходит, например, в случае встроенных массивов или при использовании некоторых библиотек сторонних производителей с интерфейсами, состоящими только из свободных функций. Максимально обобщенный код использует функции, не являющиеся членами, а не предполагает наличие функций-членов.
Например, мы можем обобщить код, с которым только что работали, в шаблон findAndInsert
следующим образом:
template
void findAndInsert(C& container, // В container находит
const V& targetVal, // первое значение
const V& insertVal) // targetVal, затем
{ // вставляет insertVal
using std::cbegin;
using std::cend;
auto it = std::find( cbegin(container), // Не член cbegin
cend(container), // Не член cend
targetVal);
container.insert(it, insertVal);
}
Этот код прекрасно работает в С++14, но, к сожалению, не в С++11. Из-за недосмотра в процессе стандартизации в С++11 добавлены не являющиеся членами функции begin
и end
, но не были добавлены cbegin
, cend
, rbegin
, rend
, crbegin
и crend
. С++ 14 исправляет это упущение.
Если вы используете С++11, хотите написать максимально обобщенный код и ни одна из используемых вами библиотек не предоставляет отсутствующие шаблоны для cbegin
и подобных функций, не являющихся шаблонами, можете легко написать собственные реализации. Например, вот как выглядит реализация функции cbegin
, не являющейся членом:
template
auto cbegin(const С& container)->decltype(std::begin(container)) {
return std::begin(container); // См. пояснения ниже
}
Вы удивлены тем, что эта функция cbegin
не вызывает функцию-член cbegin
, не так ли? Я тоже был удивлен. Но давайте подумаем логически. Этот шаблон cbegin
принимает аргументы любого типа, представляющего контейнерообразную структуру данных С, и обращается к этому аргументу через ссылку на константный объект container
. Если С — обычный тип контейнера (например, std::vector
), то container
будет ссылкой на const
-версию этого контейнера (например, const std::vector&
). Вызов функции begin
, не являющейся членом (и предоставленной С++11), для константного контейнера дает константный итератор const_iterator
, и именно этот итератор и возвращает наш шаблон. Преимущества такой реализации в том, что она работает даже для контейнеров, которые предоставляют функцию-член begin
(которую функция С++11 begin
, не являющаяся членом, вызывает для контейнеров), но не имеют функции-члена cbegin
. Таким образом, вы можете использовать эту свободную функцию cbegin
с контейнерами, поддерживающими единственную функцию-член begin
.
Этот шаблон работает и в случае, когда С представляет собой встроенный массив. При этом container
становится ссылкой на константный массив. C++11 предоставляет для массивов специализированную версию функции begin
, не являющейся членом, которая возвращает указатель на первый элемент массива. Элементы константного массива являются константами, так что указатель, который возвращает свободная функция begin
, возвращает для константного массива указатель на константу, который фактически и является const_iterator
для массива. (Для понимания, как специализировать шаблон для встроенных массивов, обратитесь к обсуждению в разделе 1.1 вывода типов в шаблонах, которые получают в качестве параметров ссылки на массивы.)
Интервал:
Закладка: