Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Универсальные ссылки возникают в двух контекстах. Наиболее распространенным являются параметры шаблона функции, такие как в приведенном выше примере кода:
template
void f(T &¶m); // param - универсальная ссылка
Вторым контекстом являются объявления auto
, включая объявление из приведенного выше примера кода:
auto &&var2 = var1; // var2 - универсальная ссылка
Общее в этих контекстах — наличие вывода типа . В шаблоне f
выводится тип param
, а в объявлении var2
выводится тип переменной var2
. Сравните это с приведенными далее примерами (также взятыми из приведенного выше примера кода), в которых вывод типа отсутствует. Если вы видите “ T&&
” без вывода типа, то вы смотрите на rvalue-ссылку:
void f(Widget &¶m); // Вывод типа отсутствует;
// param - rvalue-ссылка
Widget &&var1 = Widget(); // Вывод типа отсутствует;
// var1 - rvalue-ссылка
Поскольку универсальные ссылки являются ссылками, они должны быть инициализированы. Инициализатор универсальной ссылки определяет, какую ссылку она представляет: lvalue-ссылку или rvalue-ссылку. Если инициализатор представляет собой rvalue, универсальная ссылка соответствует rvalue-ссылке. Если же инициализатор является lvalue, универсальная ссылка соответствует lvalue-ссылке. Для универсальных ссылок, которые являются параметрами функций, инициализатор предоставляется в месте вызова:
template
void f(T&& param); // param является универсальной ссылкой
Widget w;
f( w); // В f передается lvalue; тип param -
// Widget& (т.e. Lvalue-ссылка)
f( std::move(w)); // В f передается rvalue; тип param -
// Widget&& (т.e. rvalue-ссылка)
Чтобы ссылка была универсальной, вывод типа необходим, но не достаточен. Вид объявления ссылки также должен быть корректным, и этот вид достаточно ограничен. Он должен в точности имеет вид “ T&&
”. Взглянем еще раз на пример, который мы уже рассматривали ранее:
template
void f( std::vector&¶m); // param - rvalue-ссылка
Когда вызывается f
, тип T
выводится (если только вызывающий код явно его не укажет, но этот крайний случай мы не рассматриваем). Однако объявление типа param
не имеет вида “ T&&
”; оно представляет собой std::vector&&
. Это исключает возможность для param быть универсальной ссылкой. Следовательно, param
является rvalue-ссылкой, что ваши компиляторы с удовольствием подтвердят при попытке передать в функцию f
lvalue:
std::vector v;
f(v); // Ошибка! Невозможно связать lvalue
// с rvalue-ссылкой
Даже простого наличия квалификатора const
достаточно для того, чтобы отобрать у ссылки звание универсальной:
template
void f( constT&& param); // param - rvalue-ссылка
Если вы находитесь в шаблоне и видите параметр функции с типом “ T&&
”, вы можете решить, что перед вами универсальная ссылка. Но вы не должны этого делать, поскольку размещение в шаблоне не гарантирует наличие вывода типа. Рассмотрим следующую функцию-член push_back
в std::vector
:
template
class Allocator = allocator> // Из стандарта С++
class vector {
public:
void push_back( T&&x);
…
};
Параметр push_back
, определенно, имеет верный для универсальной ссылки вид, но в данном случае вывода типа нет. Дело в том, что push_back
не может существовать без конкретного инстанцированного вектора, частью которого он является; а тип этого инстанцирования полностью определяет объявление push_back
. Другими словами, код
std::vector v;
приводит к следующему инстанцированию шаблона std::vector
:
class vector> {
public:
void push_back( Widget&&x); // rvalue-ссылка
…
};
Теперь вы можете ясно видеть, что push_back
не использует вывода типа. Эта функция push_back
для vector
(их две — данная функция перегружена) всегда объявляет параметр типа “rvalue-ссылка на T
”.
В противоположность этому концептуально подобная функция-член emplace_back
в std::vector
выполняет вывод типа:
template
class Allocator = allocator> // Из стандарта С++
class vector {
public:
template
void emplace_back( Args&&... args);
…
};
Здесь параметр типа Args
не зависит от параметра типа вектора T
, так что Args
должен выводиться при каждом вызове emplace_back
. (Да, в действительности Args
представляет собой набор параметров, а не параметр типа, но для нашего рассмотрения его можно рассматривать как параметр типа.)
Тот факт, что параметр типа emplace_back
имеет имя Args
и является при этом универсальным указателем, только усиливает мое замечание о том, что универсальная ссылка обязана иметь вид “ T&&
”. Вы не обязаны использовать в качестве имени T
. Например, приведенный далее шаблон принимает универсальную ссылку, поскольку она имеет правильный вид (“ type&&
”), а тип param
выводится (опять же, исключая крайний случай, когда вызывающий код явно указывает тип):
template // param является
void someFunc( MyTemplateType&¶m); // универсальной ссылкой
Ранее я отмечал, что переменные auto
также могут быть универсальными ссылками. Чтобы быть более точным, переменные, объявленные с типом auto&&
, являются универсальными ссылками, поскольку имеет место вывод типа и они имеют правильный вид (“ T&&
”). Универсальные ссылки auto
не так распространены, как универсальные ссылки, используемые в качестве параметров шаблонов функций, но они также время от времени возникают в C++11. Гораздо чаще они возникают в C++14, поскольку лямбда-выражения С++ 14 могут объявлять параметры auto&&
. Например, если вы хотите написать лямбда-выражение С++14 для записи времени, которое занимает вызов произвольной функции, можете сделать это следующим образом:
auto timeFuncInvocation =
[](auto&& func, auto&&... params) // С++14
{
Запуск таймера;
std::forward(func)( // Вызов func
std::forward(params)... // с params
);
Останов таймера и запись времени;
};
Если ваша реакция на код “ std::forward
” внутри лямбда-выражения — “Что за @#$%?!!”, то, вероятно, вы просто еще не читали раздел 6.3. Не беспокойтесь о нем. Главное для нас сейчас в этом лямбда-выражении — объявленные как auto&&
параметры. func
является универсальной ссылкой, которая может быть связана с любым вызываемым объектом, как lvalue, так и rvalue. params представляет собой нуль или несколько универсальных ссылок (т.e. набор параметров, являющихся универсальными ссылками), которые могут быть связаны с любым количеством объектов произвольных типов. В результате, благодаря универсальным ссылкам auto
, лямбда-выражение timeFuncInvocation
в состоянии записать время работы почти любого выполнения функции. (Чтобы разобраться в разнице между “любого” и “почти любого”, обратитесь к разделу 5.8.)
Интервал:
Закладка: