Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
auto f = [](auto&& x)
{ return normalize(std::forward< decltype(x)>(x)); };
Чтобы это лямбда-выражение принимало любое количество параметров, нам, по сути, надо только шесть точек, поскольку лямбда-выражения в С++14 могут быть с переменным числом аргументов:
auto f = [](auto&&... xs)
{ return normalize(std::forward(xs)...); };
• Используйте для параметров auto&&
при их прямой передаче с помощью std::forward
ключевое слово decltype
.
6.4. Предпочитайте лямбда-выражения применению std::bind
std::bind
в C++11 является преемником std::bind1st
и std::bind2nd
из С++98, но неформально этот шаблон является частью стандартной библиотеки еще с 2005 года. Именно тогда Комитет по стандартизации принял документ, известный как TR1, который включал спецификацию bind
. (В TR1 bind
находился в отдельном пространстве имен, так что обращаться к нему надо было как к std::tr1::bind
, а не к std::bind
, а кроме того, некоторые детали его интерфейса были иными.) Эта история означает, что некоторые программисты имеют за плечами десятилетний опыт использования std::bind
. Если вы один из них, вы можете не быть склонными отказываться от столь долго верой и правдой служившего вам инструмента. Это можно понять, и все же в данном случае лучше его сменить, поскольку лямбда-выражения С++ 11 почти всегда являются лучшим выбором, чем std::bind
. Что касается С++14, то здесь лямбда-выражения являются настоящим кладом.
В этом разделе предполагается, что вы хорошо знакомы с std::bind
. Если это не так, вы, вероятно, захотите получить базовые знания о нем, прежде чем продолжить чтение. Что ж, это похвально, тем более что никогда не знаешь, не встретишься ли с применением std::bind
в коде, который придется читать или поддерживать.
Как и в разделе 6.2, я называю функциональные объекты, возвращаемые std::bind
, bind-объектами .
Наиболее важная причина, по которой следует предпочитать лямбда-выражения, заключается в их большей удобочитаемости. Например, предположим, что у нас есть функция для настройки будильника:
// Тип для момента времени (см. синтаксис в разделе 3.3)
using Time = std::chrono::steady_clock::time_point;
// См. "enum class" в разделе 3.4
enum class Sound { Веер, Siren, Whistle };
// Тип для продолжительности промежутка времени
using Duration = std::chrono::steady_clock::duration;
// В момент t издать звук s продолжительностью d
void setAlarm(Time t, Sound s, Duration d);
Далее предположим, что в некоторой точке программы мы определили, что хотим, чтобы будильник был отключен в течение часа, после чего подал сигнал продолжительностью 30 с. Сам звук остается неопределенным. Мы можем написать лямбда-выражение, которое изменяет интерфейс setAlarm
так, что необходимо указать только звук:
// setSoundL ("L" означает "лямбда-выражение") – функциональный
// объект, позволяющий указать сигнал будильника, который должен
// звучать через час в течение 30 с
auto setSoundL =
[](Sound s) {
// Делает доступными компоненты std::chrono
using namespace std::chrono;
setAlarm(steady_clock::now()+hours(1),// Будильник через
s, // час, звучит
seconds(30)); // 30 секунд
};
Я выделил вызов setAlarm
в лямбда-выражении. Он выглядит, как обычный вызов функции, и даже читатель с малым опытом в лямбда-выражениях может понять, что переданный лямбда-выражению параметр s
передается в качестве аргумента функции setAlarm
.
В С++14 этот код можно упростить, используя стандартные суффиксы для секунд ( s
), миллисекунд ( ms
), часов ( h
) и других единиц, основанных на поддержке пользовательских литералов в С++14. Эти суффиксы определены в пространстве имен std::literals
, так что приведенный выше код переписывается как
auto setSoundL =
[](Sound s) {
using namespace std::chrono;
using std::literals; // Суффиксы С++14
setAlarm(steady_clock::now() + 1h, // С++14, смысл
s, // тот же, что
30s); // и выше
};
Наша первая попытка написать соответствующий вызов std::bind
приведена ниже. Она содержит ошибки, которые мы вскоре исправим, но главное — что правильный код более сложен, и даже эта упрощенная версия приводит к ряду важных вопросов:
using namespace std::chrono; // Как и ранее
using namespace std::literals;
using namespace std::placeholders; // Необходимо для "_1"
auto setSoundB = // "В" означает "bind"
std::bind(setAlarm,
steady_clock::now() + 1h, // Ошибка! См. ниже
_1,
30s);
Я хотел бы выделить вызов setAlarm
, как делал это в лямбда-выражении, но здесь нет вызова, который можно было бы выделить. Читатели этого кода просто должны знать, что вызов setSoundB
приводит к вызову setAlarm
со временем и продолжительностью, указанными в вызове std::bind
. Для непосвященных заполнитель “ _1
” выглядит магически, но даже знающие читатели должны в уме отобразить число в заполнителе на позицию в списке параметров std::bind
, чтобы понять, что первый аргумент вызова setSoundB
передается в setAlarm
в качестве второго аргумента. Тип этого аргумента в вызове std::bind
не определен, так что читатели должны проконсультироваться с объявлением setAlarm
, чтобы выяснить, какой аргумент передается в setSoundB
.
Но, как я уже говорил, этот код не совсем верен. В лямбда-выражении очевидно, что выражение “ steady_clock::now()+1h
” представляет собой аргумент setAlarm
. Оно будет вычислено при вызове setAlarm
. Это имеет смысл: мы хотим, чтобы будильник заработал через час после вызова setAlarm
. Однако в вызове std::bind
выражение “ steady_clock::now() + 1h
” передается в качестве аргумента в std::bind
, а не в setAlarm
. Это означает, что выражение будет вычислено при вызове std::bind
и полученное время будет храниться в сгенерированном bind-объекте. В результате будильник сработает через час после вызова std::bind
, а не через час после вызова setAlarm
!
Решение данной проблемы требует указания для std::bind
отложить вычисление выражения до вызова setAlarm
, и сделать это можно с помощью вложения еще двух вызовов std::bind
в первый:
auto setSoundB =
std::bind(setAlarm,
std::bind(std::plus<>(),
std::bind(steady_clock::now),
1h),
_1,
30s);
Если вы знакомы с шаблоном std::plus
из С++98, вас может удивить то, что между угловыми скобками не указан тип, т.e. что код содержит “ std::plus<>
” а не “ std::plus
”. В C++14 аргумент типа шаблона для шаблонов стандартных операторов в общем случае может быть опущен, так что у нас нет необходимости указывать его здесь. С++11 такой возможности не предоставляет, так что в С++11 эквивалентный лямбда-выражению std::bind
имеет следующий вид:
Интервал:
Закладка: