Удивительные возможности современного C++, которые полезно знать каждому разработчику
В статье будут рассмотрены некоторые интересные функциональные возможности C++, которые начали появляться с 11 версии языка.
44К открытий45К показов
Было время, когда С++ не хватало динамизма, и увлечься этим языком было трудно. Но всё изменилось, когда было принято решение развить стандарт C++.
С 2011 года язык стал более динамичным и постоянно развивается. Не поймите неправильно, C++ — всё ещё один из самых сложных языков. Но с того времени он стал для программиста удобнее, чем в предыдущих версиях.
В статье мы рассмотрим некоторые интересные функциональные возможности языка.
Ключевое слово auto
Когда в 11 версии C++ только появилось auto
, жизнь стала намного легче.
Идея auto
состояла в том, чтобы заставить компилятор C++ определять тип ваших данных во время компиляции, вместо того чтобы заставлять вас каждый раз объявлять тип. Это было удобно, если у вас были типы данных вроде map<string, vector <pair <int, int>>>
?
Посмотрите на строку номер 5. Вы не можете объявить что-либо без инициализатора. Строка 5 не сообщает компилятору, каким может быть тип данных.
Изначально auto
было несколько ограничено. Затем, в более поздних версиях языка, у него появилось больше возможностей.
В строках 7 и 8 была использована инициализация в скобках. Эта функция также была добавлена в 11 версии C++.
Не забывайте, что в случае использования auto
у компилятора должен быть способ определить ваш тип.
Теперь встаёт хороший вопрос, что произойдёт, если мы напишем auto a = {1, 2, 3}
? Это ошибка компиляции? Это вектор?
На самом деле, в 11 версии C++ был представлен std::initializer_list<type>
. Инициализированный список в скобках будет считаться легковесным контейнером, если объявлен как auto
.
И как упоминалось ранее, определять типы объектов компилятором полезно, когда у вас есть сложные структуры данных:
Не забудьте проверить строку 25! Выражение auto [v1, v2] = itr.second
— новая функция в 17 версии C++. Это называется структурным связыванием. В предыдущих версиях приходилось извлекать каждую переменную отдельно. Но структурное связывание сделало этот процесс более удобным.
Более того, если вы хотите получить данные, используя ссылку, то просто добавьте символ — auto &[v1, v2] = itr.second
.
Лямбда-выражение
В 11 версии C++ появились лямбда-выражения. Это что-то вроде анонимных функций в JavaScript. Они являются безымянными функциональными объектами и захватывают переменные в различных областях на основе некоторого краткого синтаксиса. Они также могут быть присвоены переменным.
Лямбды будут полезны, если вам нужно сделать в коде быстрое и небольшое изменение, и вы не хотите писать для этого отдельную функцию. Другое довольно распространённое использование функции — сравнение.
Приведённый выше пример может многое сказать.
Во-первых, обратите внимание, как фигурные скобки упрощают вам жизнь. Затем следуют универсальные begin()
, end()
, которые тоже были добавлены в 11 версии. После идёт лямбда-выражение в качестве компаратора ваших данных. Параметры лямбда-выражения объявлены с помощью auto
, что было добавлено в 14 версии С++. До этого auto
нельзя было использовать в качестве параметров функции.
Обратите внимание, мы начинаем лямбда-выражение с квадратных скобок [ ]
. Они определяют область действия лямбды — сколько у неё полномочий над локальными переменными и объектами.
Как определено в этом потрясающем репозитории по современному C++:
[ ]
— ничего не захватывает. Таким образом, вы не можете использовать любую локальную переменную внешней области видимости в лямбда-выражении. Вы можете использовать только параметры.[=]
— захватывает локальные объекты (локальные переменные, параметры) в области видимости по значению. Вы можете использовать, но не изменять их.[&]
— захватывает локальные объекты (локальные переменные, параметры) в области видимости по ссылке. Вы можете изменить их, как в примере, приведённом ниже.[this]
— захватывает этот указатель по значению.[a, &b]
— захватывает объектa
по значению, объектb
по ссылке.
Так что, если внутри лямбда-функции вы хотите преобразовать данные в какой-то другой формат, вы можете применить её, воспользовавшись преимуществами области видимости. Например:
В приведённом выше примере, если вы захватили локальные переменные по значению ([factor]
) в лямбда-выражении, то вы не можете изменить factor
в 5 строке. Вы просто не имеете права делать это. Не злоупотребляйте своими правами!
Наконец, обратите внимание, что мы берём переменную val
в качестве ссылки. Это гарантирует, что любое изменение внутри лямбда-функции фактически изменяет vector
.
Инициализатор в if и switch
Вам точно понравится эта возможность в С++ 17.
Очевидно, теперь вы можете выполнять инициализацию переменных и проверять условие сразу внутри блоков if
или switch
. Это поможет сделать код лаконичным и чистым. Общая форма:
Компиляция и constexpr
Скажем, у вас есть какое-то выражение для оценки, и его значение не изменится после инициализации. Вы можете предварительно рассчитать значение, а затем использовать его в качестве макроса. Или, как предложил C++ 11, можно использовать constexpr
.
Программисты стремятся максимально сократить время выполнения программ. Поэтому если некоторые операции можно отдать на выполнение компилятору, это стоит сделать.
Приведённый выше код — распространённый пример использования constexpr
.
Поскольку мы объявили функцию вычисления Фибоначчи как constexpr
, компилятор может предварительно вычислить fib(20)
во время компиляции. Так что после неё он может заменить строку с
на
Обратите внимание, что переданный аргумент является константным значением. Важный момент: в функциях, объявленных constexpr
, передаваемые аргументы также должны быть constexpr
или const
. В противном случае они будут вести себя как обычные функции, и во время компиляции предварительный расчёт выполняться не будет.
Переменные также могут быть constexpr
. В этом случае, как вы можете догадаться, эти переменные должны вычисляться во время компиляции. Иначе вы получите ошибку компиляции.
Интересно, что позже в C++ 17 были представлены constexpr-if
и constexpr-lambda
.
Кортежи
Как и пара, кортеж представляет собой набор значений фиксированного размера для различных типов данных.
Иногда удобнее использовать std::array
вместо кортежа. Такой массив подобен обычному массиву в C вместе с несколькими функциями стандартной библиотеки C++. Эта структура данных была добавлена в 11 версии C++.
Вывод типов шаблонных параметров для классов
Очень подробное название для функции. Идея состоит в том, что с 17 версии типы шаблонных параметров будут выводиться и для стандартных шаблонных классов. Ранее это поддерживалось только для функций.
Прим. перев. В этом примере для первого элемента кортежа будет выведен тип const char *
, а не std::string
.
Выводимый тип задаётся неявно. Это становится ещё удобнее для кортежей.
Эта функция не имеет никакого смысла, если вы слабо знакомы с шаблонами в C++.
Умные указатели
Указатели могут быть адскими.
Из-за свободы, которую предоставляют такие языки, как C++, иногда становится очень легко выстрелить себе в ногу. И во многих случаях именно указатели ответственны за вред, нанесённый компьютеру.
К счастью, в C++11 появились умные указатели, которые намного удобнее, чем простые. Они помогают программистам предотвращать утечки памяти, освобождая её, когда это возможно. Они также обеспечивают исключительную безопасность.
Если же вам удобнее визуальное представление гайда, обратите внимание на нашу дорожную карту по C++ для начинающих.
44К открытий45К показов