Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Но прямая передача имеет свои недостатки. Один из них тот, что некоторые виды аргументов не могут быть переданными прямой передачей, несмотря на то что они могут быть переданы функциям, принимающим конкретные типы. Эти сбои прямой передачи исследуются в разделе 5.8.
Второй неприятностью является запутанность сообщений об ошибках, когда клиенты передают недопустимые аргументы. Предположим, например, что клиент, создающий объект Person
, передает строковый литерал, составленный из символов char16_t
(тип С++11 для представления 16-разрядных символов) вместо char
(из которых состоит std::string
):
Person p(u"Konrad Zuse"); // "Konrad Zuse" состоит из
// символов типа const char16_t
При использовании первых трех из рассмотренных в данном разделе подходов компиляторы увидят, что доступные конструкторы могут принимать либо int
, либо std::string
, и выведут более или менее понятное сообщение об ошибке, поясняющее, что не существует преобразования из const char16_t[12]
в int
или std::string
.
Однако при подходе с использованием прямой передачи массив const char16_t
связывается с параметром конструктора без замечаний и жалоб. Оттуда он передается конструктору члена-данных типа std::string
класса Person
, и только в этот момент обнаруживается несоответствие между тем, что было передано (массив const char16_t
), и тем, что требовалось (любой тип, приемлемый для конструктора std::string
). В результате получается впечатляющее сообщение об ошибке. Так, в одном из компиляторов оно состояло более чем из 160 строк!
В этом примере универсальная ссылка передается только один раз (из конструктора Person
в конструктор std::string
), но чем более сложна система, тем больше вероятность того, что универсальная ссылка передается через несколько слоев вызовов функций до того, как достигнет точки, в которой определяется приемлемость типа аргумента (или типов). Чем большее количество раз будет передаваться универсальная ссылка, тем более непонятными и громоздкими будут выглядеть сообщения об ошибках, если что-то пойдет не так. Многие разработчики считают, что одно это является основанием для применения универсальных ссылок только там, где особенно важна производительность.
В случае класса Person
мы знаем, что параметр универсальной ссылки передающей функции должен выступать в роли инициализатора для std::string
, так что мы можем использовать static_assert
для того, чтобы убедиться в его пригодности для этой роли. Свойство типа std::is_constructible
выполняет проверку времени компиляции того, может ли объект одного типа быть построен из объекта (или множества объектов) другого типа (или множества типов), так что написать такую проверку несложно:
class Person {
public:
template< // Как и ранее
typename T,
typename = std::enable_if_t<
!std::is_base_of>::value
&&
!std::is_integral>::value
>
>
explicit Person(T&& n)
: name(std::forward(n)) {
// Проверка возможности создания std::string из T
static_assert(
std::is_constructible::value,
"Параметр n не может использоваться для "
"конструирования std::string"
};
… // Здесь идет код обычного конструктора
}
… // Остальная часть класса Person (как ранее)
};
В результате при попытке клиентского кода создать объект Person
из типа, непригодного для построения std::string
, будет выводиться указанное сообщение об ошибке. К сожалению, в этом примере static_assert
находится в теле конструктора, а передающий код, являясь частью списка инициализации членов, ему предшествует. Использовавшиеся мною компиляторы выводят ясное и понятное сообщение об ошибке от static_assert
, но только после обычных сообщений (всех этих 160 с лишним строк).
• Альтернативы комбинации универсальных ссылок и перегрузки включают использование различных имен, передачу параметров как lvalue-ссылок на const
, передачу параметров по значению и использование диспетчеризации дескрипторов.
• Ограничение шаблонов с помощью std::enable_if
позволяет использовать универсальные ссылки и перегрузки совместно, но управляет условиями, при которых компиляторы могут использовать перегрузки с универсальными ссылками.
• Параметры, представляющие собой универсальные ссылки, часто имеют преимущества высокой эффективности, но обычно их недостатком является сложность использования.
5.6. Свертывание ссылок
В разделе 5.1 говорилось, что, когда аргумент передается в шаблонную функцию, выведенный для параметра шаблона тип указывает, является ли аргумент lvalue или rvalue. В разделе не было упомянуто, что это происходит только тогда, когда аргумент используется для инициализации параметра, являющегося универсальной ссылкой, но тому есть уважительная причина: универсальные ссылки появились только в разделе 5.2. Вместе эти наблюдения об универсальных ссылках и кодировании lvalue/rvalue означают, что для шаблона
template
void func(T&& param);
выведенный параметр шаблона T
будет включать информацию о том, был ли переданный в param
аргумент lvalue или rvalue.
Механизм этого кодирования прост. Если в качестве аргумента передается lvalue, T
выводится как lvalue-ссылка. При передаче rvalue вывод типа приводит к тому, что T
не является ссылкой. (Обратите внимание: lvalue кодируются как lvalue-ссылки, но rvalue кодируются как не ссылки .) Следовательно:
Widget widgetFactory(); // Функция, возвращающая rvalue
Widget w; // Переменная (lvalue)
func(w); // Вызов функции с lvalue; тип T
// представляет собой Widget&
func(widgetFactory()); // Вызов функции с rvalue; тип T
// представляет собой Widget
В обоих вызовах func
передается Widget
, но так как один Widget
является lvalue, а второй представляет собой rvalue, для параметра шаблона T
выводятся разные типы. Это, как вы вскоре увидите, и определяет, чем становятся универсальные ссылки: rvalue- или lvalue-ссылками; а кроме того, это механизм, лежащий в основе работы std::forward
.
Прежде чем более внимательно рассмотреть std::forward
и универсальные ссылки, мы должны заметить, что ссылка на ссылку в С++ не существует. Попытайтесь объявить ее, и компилятор вынесет вам строгий выговор:
int x;
…
auto & &rx = x; // Ошибка! Объявлять ссылки на ссылки нельзя
Но рассмотрим, что произойдет, если передать lvalue шаблону функции, принимающему универсальную ссылку:
template
void func(T&& param); // Как и ранее
func(w); // Вызов func с lvalue;
Интервал:
Закладка: