Скотт Мейерс - Эффективный и современный С++. 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 // В пространстве имен std
typename remove_reference::type&&
move(T&& param) {
using ReturnType = // Объявление псевдонима; см. раздел 3.3
typename remove_reference::type&&
return static_cast(param);
}
Я выделил здесь две части кода. Одна — имя функции, потому что спецификация возвращаемого типа достаточно запутанна, и я бы не хотел, чтобы вы в ней заблудились. Вторая — приведение, которое составляет сущность функции. Как вы можете видеть, std::move
получает ссылку на объект (чтобы быть точным — универсальную ссылку; см. раздел 5.2) и возвращает ссылку на тот же объект.
Часть &&
возвращаемого типа функции предполагает, что std::move
возвращает rvalue-ссылку, но, как поясняется в разделе 5.6, если тип T
является lvalue-ссылкой, T&&
становится lvalue-ссылкой. Чтобы этого не произошло, к T
применяется свойство типа (см. раздел 3.3) std::remove_reference
, тем самым обеспечивая применение “ &&
” к типу, не являющемуся ссылкой. Это гарантирует, что std::move
действительно возвращает rvalue-ссылку, и это важно, поскольку rvalue-ссылки, возвращаемые функциями, являются rvalue. Таким образом, std::move
приводит свой аргумент к rvalue, и это все, что она делает.
В качестве небольшого отступления скажем, что std::move
можно реализовать в С++14 меньшими усилиями. Благодаря выводу возвращаемого типа функции (см. раздел 1.3) и шаблону псевдонима std::remove_reference_t
(см. раздел 3.3) std::move
можно записать следующим образом:
template // С++14; находится в
decltype(auto)move(T&& param) // пространстве имен std
{
using ReturnType = remove_reference_t&&
return static_cast(param);
}
Легче для восприятия, не так ли?
Поскольку std::move
ничего не делает, кроме приведения своего аргумента к rvalue, были предложения дать этому шаблону другое имя, например rvalue_cast
. Как бы там ни было, у нас имеется имя std::move
, так что нам важно запомнить, что это имя std::move
делает и чего не делает. Итак, оно выполняет приведение. И оно ничего не переносит.
Конечно, rvalue являются кандидатами на перемещение, поэтому применение std::move
для объекта сообщает компилятору, что объект предназначается для перемещения. Вот почему std::move
имеет такое имя: чтобы легко распознавать объекты, которые могут быть перемещены.
По правде говоря, rvalue обычно являются всего лишь кандидатами для перемещения. Предположим, что вы пишете класс, представляющий аннотации. Конструктор класса получает параметр std::string
, представляющий аннотацию, и копирует значение параметра в член-данные. С учетом информации из раздела 8.1 вы объявляете параметр как передаваемый по значению:
class Annotation {
public:
explicit Annotation( std::stringtext); // Параметр
… // копируемый, так что согласно
}; // разделу 8.1 он передается по значению
Но конструктору Annotation
требуется только прочесть значение text
. Ему не нужно его модифицировать. В соответствии с освященной веками традицией использования const
везде, где только можно, вы переписываете свое объявление, делая text
константой:
class Annotation
public:
explicit Annotation( conststd::string text);
…
};
Чтобы избежать дорогостоящей операции копирования text
в член-данные, вы оставляете в силе совет из раздела 8.1 и применяете std::move
к text
, тем самым получая rvalue:
class Annotation {
public:
explicit Annotation(const std::string text)
: value( std::move(text)) // "Перемещение" text в value;
{ … } // этот код не делает того,
… // что от него ожидается!
private:
std::string value;
};
Этот код компилируется. Этот код компонуется. Этот код выполняется. Этот код устанавливает значение члена-данных value
равным содержимому строки text
. Единственное, что отличает этот код от идеальной реализации ваших намерений, — то, что text
не перемещается в value
, а копируется . Конечно, text
приводится к rvalue с помощью std::move
, но text
объявлен как const std::string
, так что перед приведением text
являлся lvalue типа const std::string
, так что результатом приведения является rvalue типа const std::string
, и на протяжении всех этих действий константность сохраняется.
Рассмотрим, как компиляторы определяют, какой из конструкторов std::string
должен быть вызван. Есть две возможности:
class string { // std::string в действительности представляет
public: // собой typedef для std::basic_string
…
string(const string& rhs); // Копирующий конструктор
string(string&& rhs); // Перемещающий конструктор
};
В списке инициализации членов конструктора Annotation
результатом std::move(text)
является rvalue типа const std::string
. Это rvalue нельзя передать перемещающему конструктору std::string
, поскольку перемещающий конструктор получает rvalue-ссылку на неконстантную std::string
. Однако это rvalue может быть передано копирующему конструктору, поскольку lvalue-ссылку на const
разрешено связывать с константным rvalue. Таким образом, инициализация члена использует копирующий конструктор std::string
, несмотря на то что text
был приведен к rvalue! Такое поведение имеет важное значение для поддержания корректности const
. Перемещение значения из объекта в общем случае модифицирует этот объект, так что язык программирования должен не разрешать передавать константные объекты в функции (такие, как перемещающие конструкторы), которые могут их модифицировать.
Из этого примера следует извлечь два урока. Во-первых, не объявляйте объекты как константные, если хотите иметь возможность выполнять перемещение из них. Запрос перемещения к константным объектам молча трансформируется в копирующие операции. Во-вторых, std::move
не только ничего не перемещает самостоятельно, но даже не гарантирует, что приведенный этой функцией объект будет иметь право быть перемещенным. Единственное, что точно известно о результате применения std::move
к объекту, — это то, что он является rvalue.
История с std::forward
подобна истории с std::move
, но тогда как std::move выполняет безоговорочное приведение своего аргумента в rvalue, std::forward
делает это только при определенных условиях. std::forward
является условным приведением. Чтобы понять, когда приведение выполняется, а когда нет, вспомним, как обычно используется std::forward
. Наиболее распространенным сценарием является тот, когда шаблон функции получает параметр, представляющий собой универсальную ссылку, и который передается другой функции:
void process(const Widget& lvalArg); // Обработка lvalue
Интервал:
Закладка: