Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Но невозможность предварительного объявления enum
имеет свои недостатки. Наиболее важным из них, вероятно, является увеличение зависимостей при компиляции. Вновь обратимся к перечислению Status
:
enum Status { good = 0,
failed = 1,
incomplete = 100,
corrupt = 200,
indeterminate = 0xFFFFFFFF
};
Это разновидность перечисления, которая, скорее всего, будет использоваться во всей системе, а следовательно, включенная в заголовочный файл, от которой зависит каждая из частей системы. Если добавить новое значения состояния
enum Status { good = 0,
failed = 1,
incomplete = 100,
corrupt = 200,
audited = 500,
indeterminate = 0xFFFFFFFF
};
то, вероятно, придется перекомпилировать всю систему полностью, даже если только одна подсистема — возможно, одна-единственная функция! — использует это новое значение. Это одна из тех вещей, которые программисты просто ненавидят . И это та вещь, которую исключает возможность предварительного объявления enum
в С++11. Например, вот совершенно корректное объявление enum
с областью видимости, и функции, которая получает его в качестве параметра:
enum class Status; // Предварительное объявление
void continueProcessing(Status s); // и его использование
Заголовочный файл, содержащий эти объявления, не требует перекомпиляции при пересмотре определения Status
. Кроме того, если изменено перечисление Status
(например, добавлено значение audited
), но поведение continueProcessing
не изменилось (например, потому что continueProcessing
не использует значение audited
), то не требуется и перекомпиляция реализации continueProcessing
.
Но если компилятор должен знать размер enum
до использования, то как могут перечисления С++ 11 быть предварительно объявлены, в то время как перечисления С++98 этого не могут? Ответ прост: базовый тип перечислений с областью видимости всегда известен, а для перечислений без областей видимости вы можете его указать.
По умолчанию базовым типом для enum
с областью видимости является int
:
enum class Status; // Базовый тип — int
Если вас не устраивает значение по умолчанию, вы можете его перекрыть:
enum class Status: std::uint32_t; // Базовый тип для Status -
// std::uint32_t (из )
В любом случае компиляторы знают размер перечислителей в перечислении с областью видимости.
Чтобы указать базовый тип для перечисления без области видимости, вы делаете то же, что и для перечисления с областью видимости, и полученный результат может быть предварительно объявлен:
enum Color: std::uint8_t; // Предварительное объявление
// перечисления без области видимости;
// базовый тип - std::uint8_t
Спецификация базового типа может быть указана и в определении enum
:
enum class Status: std::uint32_t{ good = 0,
failed = 1,
incomplete = 100,
corrupt = 200,
audited = 500,
indeterminate = 0xFFFFFFFF
};
С учетом того факта, что enum
с областью видимости устраняет загрязнение пространства имен и невосприимчиво к бессмысленным преобразованиям типов, вас может удивить тот факт, что имеется как минимум одна ситуация, в которой могут быть полезны перечисления без области видимости, а именно — при обращении к полям в кортежах C++11 std::tuple
. Предположим, например, что у нас есть кортеж, содержащий имя, адрес электронной почты и значение репутации пользователя на сайте социальной сети:
using UserInfo = // Псевдоним типа; см. раздел 3.3
std::tuple
std::string, // Адрес
std::size_t> ; // Репутация
Хотя комментарии указывают, что представляет собой каждое поле кортежа, это, вероятно, не слишком полезно, когда вы сталкиваетесь с кодом наподобие следующего в отдельном исходном файле:
UserInfo uInfo; // Объект с типом кортежа
…
auto val = std::get<1>(uInfo); // Получение значения поля 1
Как программисту вам приходится отслеживать множество вещей. Вы действительно должны помнить, что поле 1 соответствует адресу электронной почты пользователя? Я думаю, нет. Использование enum
без области видимости для сопоставления имен полей с их номерами позволяет избежать необходимости перегружать память:
enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // Как и ранее
…
auto val = std::get< uiEmail>(uInfo); // Значение адреса
Все было бы гораздо сложнее без неявного преобразования значений из UserInfoFields
в тип std::size_t
, который является типом, требующимся для std::get
.
Соответствующий код с применением перечисления с областью видимости существенно многословнее:
enum classUserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // Как и ранее
…
auto val =
std::get< static_cast(UserInfoFields::uiMail)>
(uInfo);
Эта многословность может быть сокращена с помощью функции, которая принимает перечислитель и возвращает соответствующее значение типа std::size_t
, но это немного сложнее. std::get
является шаблоном, так что предоставляемое значение является аргументом шаблона (обратите внимание на применение не круглых, а угловых скобок), так что функция, преобразующая перечислитель в значение std::size_t
, должна давать результат во время компиляции . Как поясняется в разделе 3.9, это означает, что нам нужна функция, являющаяся constexpr
.
Фактически это должен быть constexpr
-шаблон функции, поскольку он должен работать с любыми перечислениями. И если мы собираемся делать такое обобщение, то должны обобщить также и возвращаемый тип. Вместо того чтобы возвращать std::size_t
, мы должны возвращать базовый тип перечисления. Он доступен с помощью свойства типа std::underlying_type
(о свойствах типов рассказывается в разделе 3.3). Наконец мы объявим его как noexcept
(см. раздел 3.8), поскольку мы знаем, что он никогда не генерирует исключений. В результате мы получим шаблон функции toUType
, который получает произвольный перечислитель и может возвращать значение как константу времени компиляции:
template
constexpr typename std::underlying_type::type
toUType(E enumerator) noexcept
{
return
static_cast
std::underlying_type::type>(enumerator);
}
В С++14 toUType
можно упростить заменой typename std::underlying_type::type
более изящным std::underlying_type_t
(см. раздел 3.3):
template // С++14
constexpr std::underlying_type _t
toUType(E enumerator) noexcept {
return static_cast_t>(enumerator);
}
Еще более изящный возвращаемый тип auto
(см. раздел 1.3) также корректен в С++14:
template // С++14
constexpr auto
toUType(E enumerator) noexcept {
Интервал:
Закладка: