Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
int f1(std::shared_ptr spw); // Вызывается только при
double f2(std::unique_ptr upw); // блокировке соответ-
bool f3(Widget* pw); // ствующего мьютекса
Вызывающий код с передачей нулевых указателей может выглядеть следующим образом:
std::mutex f1m, f2m, f3m; // Мьютексы для f1, f2 и f3
using MuxGuard = // C++11 typedef; см. раздел 3.3
std::lock_guard;
{
MuxGuard g(f1m); // Блокировка мьютекса для f1
auto result = f1( 0); // Передача 0 функции f1
} // Разблокирование мьютекса
…
{
MuxGuard g(f2m); // Блокировка мьютекса для f2
auto result = f2( NULL); // Передача NULL функции f2
} // Разблокирование мьютекса
…
{
MuxGuard g(f3m); // Блокировка мьютекса для f3
auto result = f3( nullptr); // Передача nullptr функции f3
} // Разблокирование мьютекса
То, что в первых двух вызовах не был передан nullptr
, грустно; тем не менее код работает, а это чего-то да стоит. Однако повторяющиеся действия еще более грустны. Они просто беспокоят. Во избежание дублирования такого вида и предназначаются шаблоны, так что давайте превратим эти действия в шаблон.
template
typename MuxType,
typename PtrType>
auto lockAndCall(FuncType func,
MuxType& mutex,
PtrType ptr) -> decltype(func(ptr)) {
using MuxGuard = std::lock_guard;
MuxGuard g(mutex);
return func(ptr);
}
Если возвращаемый тип этой функции ( auto...->decltype(func(ptr)
) заставляет вас чесать затылок, обратитесь к разделу 1.3, в котором объясняется происходящее. Там вы узнаете, что в С++ 14 возвращаемый тип можно свести к простому decltype(auto)
:
template
typename MuxType,
typename PtrType>
decltype(auto) lockAndCall(FuncType func, // С++14
MuxType& mutex,
PtrType ptr) {
using MuxGuard = std::lock_guard;
MuxGuard g(mutex);
return func(ptr);
}
Для данного шаблона lockAndCall
(любой из версий), вызывающий код может иметь следующий вид:
auto result1 = lockAndCall(f1, f1m, 0); // Ошибка!
auto result2 = lockAndCall(f2, f2m, NULL); // Ошибка!
auto result3 = lockAndCall(f3, f3m, nullptr); // OK
Такой код можно написать, но, как показывают комментарии, в двух случаях из трех этот код компилироваться не будет. В первом вызове проблема в том, что когда 0
передается в lockAndCall
, происходит вывод соответствующего типа шаблона. Типом 0
является, был и всегда будет int
, как и тип параметра ptr
в инстанцировании данного вызова lockAndCall
. К сожалению, это означает, что в вызов func
в lockAndCall
передается int
, а этот тип несовместим с параметром std::shared_ptr
, ожидаемым функцией f1
. Значение 0
, переданное в вызове lockAndCall
, призвано представлять нулевой указатель, но на самом деле передается заурядный int
. Попытка передать этот int функции f1
как std::shared_ptr
представляет собой ошибку типа. Вызов lockAndCall
с 0
оказывается неудачным, поскольку в шаблоне функции, которая требует аргумент типа std::shared_ptr
, передается значение int
.
Анализ вызова с переданным NULL
по сути такой же. Когда в функцию lockAndCall
передается NULL
, для параметра ptr
выводится целочисленный тип, и происходит ошибка, когда целочисленный тип передается функции f2
, которая ожидает аргумент типа std::unique_ptr
.
В противоположность первым двум вызовам вызов с nullptr
никакими неприятностями не отличается. Когда функции lockAndCall
передается nullptr
, выведенным типом ptr
является std::nullptr_t
. При передаче ptr
в функцию f3
выполняется неявное преобразование std::nullptr_t
в Widget*
, поскольку std::nullptr_t
неявно преобразуется во все типы указателей.
Тот факт, что вывод типа шаблона приводит к “неверным” типам для 0
и NULL
(т.e. к их истинным типам, а не к представлению с их использованием нулевых указателей), является наиболее убедительной причиной для использования nullptr
вместо 0
или NULL
, когда вы хотите представить нулевой указатель. При применении nullptr
шаблоны не представляют собой никаких особых проблем. Вместе с тем фактом, что nullptr
не приводят к неприятностям при разрешении перегрузки, которым подвержены 0
и NULL
, все это приводит к однозначному выводу — если вам нужен нулевой указатель, используйте nullptr
, но не 0
и не NULL
.
• Предпочитайте применение nullptr
использованию 0
или NULL
.
• Избегайте перегрузок с использованием целочисленных типов и типов указателей.
3.3. Предпочитайте объявление псевдонимов применению typedef
Я уверен, что мы можем сойтись на том, что применение контейнеров STL — хорошая идея, и я надеюсь, что раздел 4.1 убедит вас, что хорошей идеей является применение std::unique_ptr
, но думаю, что ни один из вас не увлечется многократным написанием типов наподобие std::unique_ptr>
. Одна мысль о таких типах лично у меня вызывает все симптомы синдрома запястного канала [3] Синдром запястного канала — неврологическое заболевание, проявляющееся длительной болью и онемением пальцев рук. Широко распространено представление, что длительная ежедневная работа на компьютере, требующая постоянного использования клавиатуры, является фактором риска развития синдрома запястного канала. — Примеч. пер .
.
Избежать такой медицинской трагедии несложно, достаточно использовать typedef
:
typedef
std::unique_ptr>
UPtrMapSS;
Но typedef
слишком уж какой-то девяносто восьмой… Конечно, он работает и в С++11, но стандарт С++11 предлагает еще и объявление псевдонима (alias declaration):
using UPtrMapSS =
std::unique_ptr>;
С учетом того, что typedef
и объявление псевдонима делают в точности одно и то же, разумно задаться вопросом “А есть ли какое-то техническое основание для того, чтобы предпочесть один способ другому?”
Да, есть, но перед тем как я его укажу, замечу, что многие программисты считают объявление псевдонима более простым для восприятия при работе с типами, включающими указатели на функции:
// FP является синонимом для указателя на функцию, принимающую
// int и const std::string& и ничего не возвращающую
typedefvoid (* FP) (int, const std::string&);
// То же самое, но как объявление псевдонима
using FP= void (*)(int, const std::string&);
Конечно, ни одна из разновидностей не оказывается существенно проще другой, а ряд программистов тратит немало времени для того, чтобы верно записать синонимы для типов указателей на функции, так что пока что убедительных причин для предпочтения объявления псевдонима пока что нет.
Читать дальшеИнтервал:
Закладка: