Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
При таком объявлении authAndAccess
возвращает тот тип, который возвращает operator[]
при применении к переданному контейнеру, в точности как мы и хотели.
С++11 разрешает вывод возвращаемых типов лямбда-выражений из одной инструкции, а С++14 расширяет эту возможность на все лямбда-выражения и все функции, включая состоящие из множества инструкций. В случае authAndAccess
это означает, что в С++ 14 мы можем опустить завершающий возвращаемый тип, оставляя только одно ведущее ключевое слово auto
. При таком объявлении auto
означает, что имеет место вывод типа. В частности, это означает, что компиляторы будут выводить возвращаемый тип функции из ее реализации:
template // С++14;
auto authAndAccess(Container& с, Index i) // Не совсем
{ // корректно
authenticateUser();
return c[i]; // возвращаемый тип выводится из c[i]
}
В разделе 1.2 поясняется, что для функций с auto
-спецификацией возвращаемого типа компиляторы применяют вывод типа шаблона. В данном случае это оказывается проблематичным. Как уже говорилось, operator[]
для большинства контейнеров с объектами типа Т
возвращает Т&
, но в разделе 1.1 поясняется, что в процессе вывода типа шаблона “ссылочность” инициализирующего выражения игнорируется. Рассмотрим, что это означает для следующего клиентского кода:
std::deque d;
…
authAndAccess(d, 5) = 10; // Аутентифицирует пользователя, воз-
// вращает d[5], затем присваивает ему
// значение 10. Код не компилируется!
Здесь d[5]
возвращает int&
, но вывод возвращаемого типа auto
для authAndAccess
отбрасывает ссылку, тем самым давая возвращаемый тип int
. Этот int
, будучи возвращаемым значением функции, является rvalue
, так что приведенный выше код пытается присвоить этому rvalue
типа int значение 10. Это запрещено в С++, так что данный код не компилируется.
Чтобы заставить authAndAccess
работать так, как мы хотим, нам надо использовать для ее возвращаемого типа вывод типа decltype
, т.е. указать, что authAndAccess
должна возвращать в точности тот же тип, что и выражение с[i]
. Защитники С++, предвидя необходимость использования в некоторых случаях правил вывода типа decltype
, сделали это возможным в С++14 с помощью спецификатора decltype(auto)
. То, что изначально может показаться противоречием ( decltype
и auto
?), в действительности имеет смысл: auto
указывает, что тип должен быть выведен, а decltype
говорит о том, что в процессе вывода следует использовать правила decltype
. Итак, можно записать authAndAccess
следующим образом:
template // С++14; работает,
decltype(auto) // но все еще
authAndAccess(Containers с, Index i) // требует
{ // уточнения
authenticateUser();
return c[i];
}
Теперь authAndAccess
действительно возвращает то же, что и c[i]
. В частности, в распространенном случае, когда c[i]
возвращает Т&
, authAndAccess
также возвращает Т&
, и в том редком случае, когда c[i]
возвращает объект, authAndAccess
также возвращает объект.
Использование decltype(auto)
не ограничивается возвращаемыми типами функций. Это также может быть удобно для объявления переменных, когда вы хотите применять правила вывода типа decltype
к инициализирующему выражению:
Widget w;
const Widget& cw = w;
automyWidget1 = cw; // Вывод типа auto:
// тип myWidget1 - Widget
decltype(auto)myWidget2 = cw; // Вывод типа decltype:
// тип myWidget2 - const Widget&
Я знаю, что вас беспокоят два момента. Один из них — упомянутое выше, но пока не описанное уточнение authAndAccess
. Давайте, наконец-то, разберемся в этом вопросе. Еще раз посмотрим на версию authAndAccess
в С++14:
template
decltype(auto) authAndAccess(Container& с, Index i);
Контейнер передается как lvalue-ссылка на неконстантный объект, поскольку возвращаемая ссылка на элемент контейнера позволяет клиенту модифицировать этот контейнер. Но это означает, что этой функции невозможно передавать контейнеры, являющиеся rvalue. rvalue невозможно связать с lvalue-ссылками (если только они не являются lvalue-ссылками на константные объекты, что в данном случае очевидным образом не выполняется).
Надо сказать, что передача контейнера, являющегося rvalue, в authAndAccess
является крайним случаем. Такой rvalue-контейнер, будучи временным объектом, обычно уничтожается в конце инструкции, содержащей вызов authAndAccess
, а это означает, что ссылка на элемент в таком контейнере (то, что должна вернуть функция authAndAccess
) окажется “висячей” в конце создавшей ее инструкции. Тем не менее передача временного объекта функции authAndAccess
может иметь смысл. Например, клиент может просто хотеть сделать копию элемента во временном контейнере:
std::deque makeStringDeque(); // Фабричная функция
// Делаем копию пятого элемента deque, возвращаемого
// функцией makeStringDeque
auto s = authAndAccess(makeStringDeque(), 5);
Поддержка такого использования означает, что мы должны пересмотреть объявление функции authAndAccess
, которая должна принимать как lvalue, так и rvalue. Можно использовать перегрузку (одна функция объявлена с параметром, представляющим собой lvalue-ссылку, а вторая — с параметром, представляющим собой rvalue-ссылку), но тогда нам придется поддерживать две функции. Избежать этого можно, если у нас будет функция authAndAccess
, использующая ссылочный параметр, который может быть связан как с lvalue, так и с rvalue, и в разделе 5.2 поясняется, что это именно то, что делают универсальные ссылки. Таким образом, authAndAccess
может быть объявлена следующим образом:
template // Теперь с -
decltype(auto) authAndAccess(Container &&с, // универсальная
Index i); // ссылка
В этом шаблоне мы не знаем, с каким типом контейнера работаем, и точно так же не знаем тип используемых им индексных объектов. Использование передачи по значению для объектов неизвестного типа обычно сопровождается риском снижения производительности из-за ненужного копирования, проблемами со срезкой объектов (см. раздел 8.1) и насмешками коллег. Но в случае индексов контейнеров, следуя примеру стандартной библиотеки для значений индексов (например, в operator[]
для std::string
, std::vector
и std::deque) это решение представляется разумным, так что мы будем придерживаться для них передачи по значению.
Однако нам нужно обновить реализацию шаблона для приведения его в соответствие с предостережениями из раздела 5.3 о применении std::forward
к универсальным ссылкам:
template // Окончательная
decltype(auto) // версия для
authAndAccess(Container&& с, Index i) // С++14
{
authenticateUser();
Интервал:
Закладка: