Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
static const
и constexpr
без определенийВ качестве общего правила не требуется определять в классах целочисленные члены-данные static const
и constexpr
; одних объявлений вполне достаточно. Дело в том, что компиляторы выполняют распространение const
для значений таких членов, тем самым устраняя необходимость выделять для них память. Например, рассмотрим такой код:
class Widget {
public:
// Объявление MinVals:
static constexpr std::size_t MinVals = 28;
};
… // Определения MinVals нет
std::vector widgetData;
widgetData.reserve( Widget::MinVals); // Использование MinVals
Здесь мы используем Widget::MinVals
(далее — просто MinVals
) для указания начальной емкости widgetData
, даже несмотря на то, что определения MinVals
нет. Компиляторы обходят отсутствующее определение (как и должны это делать) подстановкой значения 28 во все места, где упоминается MinVals
. Тот факт, что для значения MinVals
не выделена память, проблемой не является. Если берется адрес MinVals
(например, кто-то создает указатель на MinVals
), то MinVals
требует места в памяти (чтобы указателю было на что указывать), и тогда приведенный выше код хотя и будет компилироваться, не будет компоноваться до тех пор, пока не будет предоставлено определение MinVals
.
С учетом этого представим, что f
(которой функция fwd
передает аргумент) объявлена следующим образом:
void f(std::size_t val);
Вызов f
с MinVals
проблемы не представляет, поскольку компиляторы просто заменяют MinVals
его значением:
f(Widget::MinVals); // OK, рассматривается как "f(28)"
Увы, все не так хорошо, если попытаться вызвать f
через fwd
:
fwd(Widget::MinVals); // Ошибка! Не должно компоноваться
Этот код компилируется, но не должен компоноваться. Если это напоминает вам происходящее при взятии адреса MinVals
, это хорошо, потому что проблема в обоих случаях одна и та же.
Хотя нигде в исходном коде не берется адрес MinVals
, параметром fwd
является универсальная ссылка, а ссылки в коде, сгенерированном компилятором, обычно рассматриваются как указатели. В бинарном коде программы указатели и ссылки, по сути, представляют собой одно и то же. На этом уровне можно считать, что ссылки — это просто указатели, которые автоматически разыменовываются. В таком случае передача MinVals
по ссылке фактически представляет собой то же, что и передача по указателю, а раз так, то должна иметься память, на которую этот указатель указывает. Передача целочисленных членов-данных static const
и constexpr
по ссылке в общем случае требует, чтобы они были определены, и это требование может привести к неудачному применению прямой передачи там, где эквивалентный код без прямой передачи будет успешен.
Возможно, вы обратили внимание на некоторые юркие слова, употребленные мною выше. Я сказал, что код “не должен” компоноваться. Ссылки “обычно” рассматриваются как указатели. Передача целочисленных членов-данных static const
и constexpr
по ссылке “в общем случае” требует, чтобы они были определены. Это похоже на то, как будто я что-то знаю, но ужасно не хочу вам сказать…
Это потому, что так и есть. В соответствии со стандартом передача MinVals
по ссылке требует, чтобы этот член-данные был определен. Но не все реализации выполняют это требование. Так что в зависимости от ваших компиляторов и компоновщиков вы можете обнаружить, что в состоянии выполнить прямую передачу целочисленных членов-данных static const
и constexpr
, которые не были определены. Если это так — поздравляю, но нет причин ожидать, что такой код будет переносим. Чтобы сделать его переносимым, просто добавьте определение интересующего вас целочисленного члена-данных, объявленного как static const
или constexpr
. Для MinVals это определение имеет следующий вид:
constexpr std::size_t Widget::MinVals; // В .cpp-файле Widget
Обратите внимание, что в определении не повторяется инициализатор (28 в случае MinVals
). Не переживайте об этом. Если вы забудете предоставить инициализатор в обоих местах, компиляторы будут жаловаться, тем самым напоминая вам о необходимости указать его только один раз.
Предположим, что поведение нашей функции f
(которой мы хотим передать аргументы через шаблон fwd
) может настраиваться путем передачи ей некоторой функции, выполняющей определенную работу. В предположении, что эта функция получает и возвращает int
, функция f
может быть объявлена следующим образом:
void f( int (*pf)(int)); // pf - функция обработки
Стоит заметить, что f
может также быть объявлена с помощью более простого синтаксиса без указателей. Такое объявление может имеет следующий вид, несмотря на то что оно означает в точности то же, что и объявление выше:
void f( int pf(int)); // Объявление той же f, что и выше
В любом случае теперь предположим, что у нас есть перегруженная функция processVal
:
int processVal(int value);
int processVal(int value, int priority);
Мы можем передать processVal
функции f
,
f(processVal); // Без проблем
но это выглядит удивительным. Функции f
в качестве аргумента требуется указатель на функцию, но processVal
не является ни указателем на функцию, ни даже функцией; это имя двух разных функций. Однако компиляторы знают, какая processVal
нужна: та, которая соответствует типу параметра функции f
. Таким образом, они могут выбрать processVal
, принимающую один int
, а затем передать адрес этой функции в функцию f
.
Все это работает благодаря тому, что объявление f
позволяет компиляторам вывести требующуюся версию функции processVal
. Однако fwd
представляет собой шаблон функции, не имеющий информации о том, какой тип ему требуется, а потому компиляторы не в состоянии определить, какая из перегрузок должна быть передана:
fwd(processVal); // Ошибка! Какая processVal?
Само по себе имя processVal
не имеет типа. Без типа не может быть вывода типа, а без вывода типа мы получаем еще один случай неудачной прямой передачи.
Та же проблема возникает и если мы пытаемся использовать шаблон функции вместо перегруженного имени функции или в дополнение к нему. Шаблон функции представляет не единственную функцию, он представляет множество функций:
template
T workOnVal(T param) // Шаблон для обработки значений
{ … }
fwd(workOnVal); // Ошибка! Какое инстанцирование workOnVal?
Получить функцию прямой передачи наподобие fwd
, принимающую имя перегруженной функции или имя шаблона, можно, вручную указав перегрузку (или инстанцирование), которую вы хотите передать. Например, вы можете создать указатель на функцию того же типа, что и параметр f
, инициализировать этот указатель с помощью processVal
или workOnVal
(тем самым обеспечивая выбор корректной версии processVal
или генерацию корректного инстанцирования workOnVal
) и передать его шаблону fwd
:
Интервал:
Закладка: