Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Предположим, что нам нужна структура данных для хранения результатов эксперимента, который может быть проведен при разных условиях. Например, уровень освещения в ходе эксперимента может быть высоким, низким или освещение может быть отключено вовсе; может быть разная температура, и т.д. Если всего имеется n
условий, влияющих на проведение эксперимента, и у каждого по три возможных состояния, то общее количество комбинаций составит 3 ⁿ . Хранение результатов экспериментов для всех комбинаций условий требует структуры данных с достаточным количеством памяти для хранения 3 ⁿ значений. В предположении, что каждый результат представляет собой int и что n известно (или может быть вычислено) во время компиляции, подходящим выбором структуры данных может быть std::array
. Однако нам требуется способ вычисления 3 ⁿ во время компиляции. Стандартная библиотека С++ предоставляет функцию std::pow
, обеспечивающую интересующую нас математическую функциональность, но с точки зрения наших целей имеются две проблемы. Во-первых, std::pow
работает с типами с плавающей точкой, а нам нужен целочисленный результат. Во-вторых, std::pow
не является constexpr
(т.e. не гарантирует возврат времени компиляции при переданных ей значениях времени компиляции), так что мы не можем использовать ее для указания размера std::array
.
К счастью, мы можем написать функцию pow
, которая нам нужна. Как это сделать, я покажу чуть позже, но сначала давайте взглянем, каким образом эта функция может быть объявлена и использована:
constexpr // pow является constexpr
int pow(int base, int exp) noexcept // Не генерирует исключений
{
// Ее реализация - ниже
}
constexpr auto numConds = 5; // Количество условий
std::arraypow(3, numConds)> // results содержит
results; // 3^numConds элементов
Вспомним, что constexpr
перед pow
не говорит о том, что pow
возвращает константное значение; оно говорит, что если base
и exp
являются константами времени компиляции, то результат pow
может быть использован как константа времени компиляции. Если base
и/или exp
не являются константами времени компиляции, то результат pow
будет вычисляться во время выполнения. Это означает, что pow
может быть вызвана не только для вычисления во время компиляции таких вещей, как размер std::array
, но и в контексте времени выполнения, как здесь:
auto base = readFromDB("base"); // Эти значения получаются
auto exp = readFromDB("exponent"); // во время компиляции
auto baseToExp = pow(base, exp); // Вызов функции pow
// во время выполнения
Поскольку функции constexpr
должны быть способны возвращать результаты во время компиляции при вызове со значениями времени компиляции, на их реализации накладываются ограничения. Эти ограничения различны в C++11 и С++14.
В С++11 функции constexpr
могут содержать не более одной выполнимой инструкции — return
. Это выглядит более ограничивающим, чем является на самом деле, поскольку для повышения выразительности constexpr
-функций можно использовать две хитрости. Во-первых, можно применять условный оператор “ ?:
” вместо инструкции if-else
, а во-вторых, вместо циклов можно использовать рекурсию. Таким образом, функция pow может быть реализована следующим образом:
constexpr int pow(int base, int exp) noexcept {
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}
Этот код работает, но только очень непритязательный функциональный программист сможет назвать его красивым. В С++ 14 ограничения на constexpr
-функции существенно слабее, так что становится возможной следующая реализация:
constexpr int pow(int base, int exp) noexcept // С++14
{
auto result = 1;
for (int i = 0; i < exp; ++i) result *= base;
return result;
}
Функции constexpr
ограничены приемом и возвратом только литеральных типов (literal types), которые, по сути, означают типы, могущие иметь значения, определяемые во время компиляции. В С++11 к ним относятся все встроенные типы за исключением void
, но литеральными могут быть и пользовательские типы, поскольку конструкторы и прочие функции-члены также могут являться constexpr
:
class Point {
public:
constexprPoint(double xVal = 0, double yVal = 0) noexcept
: x(xVal), y(yVal) {}
constexprdouble xValue() const noexcept { return x; }
constexprdouble yValue() const noexcept { return y; }
void setX(double newX) noexcept { x = newX; }
void setY(double newY) noexcept { y = newY; }
private:
double x, y;
};
Здесь конструктор Point
может быть объявлен как constexpr
, поскольку, если переданные ему аргументы известны во время компиляции, значения членов-данных созданного Point
также могут быть известны во время компиляции. А значит, инициализированный таким образом объект Point
может быть constexpr
:
constexprPoint p1(9.4, 27.7); // OK, во время компиляции
// работает constexpr конструктор
constexprPoint p2(28.8, 5.3); // То же самое
Аналогично функции доступа xValue
и yValue
могут быть constexpr
, поскольку если они вызываются для объекта Point
со значением, известным во время компиляции (например, объект constexpr Point
), значения членов-данных x
и y
могут быть известны во время компиляции. Это делает возможным написать constexpr
-функции, которые вызывают функции доступа Point
и инициализируют constexpr
-объекты результатами вызовов этих функций:
constexpr
Point midpoint(const Points p1, const Points p2) noexcept {
return {(p1. xValue()+p2. xValue()) / 2, // Вызов constexpr
(p1. yValue()+p2. yValue()) / 2}; // функции-члена
}
constexprauto mid = midpoint(p1, p2); // Инициализация
// constexpr объекта результатом constexpr-функции
Это очень интересно. Это означает, что объект mid
может быть создан в памяти, предназначенной только для чтения, несмотря на то что его инициализация включает вызовы конструкторов, функций доступа и функции, не являющейся членом! Это означает, что вы можете использовать выражение наподобие mid.xValue() * 10
в аргументе шаблона или в выражении, определяющем значение перечислителя [10] Поскольку P oint::xValue возвращает double , типом mid.xValue() * 10 также является double . Типы с плавающей точкой не могут использоваться для инстанцирования шаблонов или для указания значений перечислений, но они могут быть использованы как части больших выражений, дающих интегральные типы. Например, для инстанцирования шаблона или для указания значения перечислителя может использоваться выражение static_cast(mid.xValue() * 10) .
! Это означает, что традиционно довольно строгая граница между работой во время компиляции и работой во время выполнения начинает размываться, и некоторые вычисления, традиционно являющиеся вычислениями времени выполнения, могут перейти на стадию компиляции. Чем больший код участвует в таком переходе, тем быстрее будет работать ваша программа. (Однако компилироваться она может существенно дольше.)
Интервал:
Закладка: