Итак, судьба снова свела вас с C++, и вы поражены его возможностями с точки зрения производительности, удобства и выразительности кода. Но вот незадача: вы теряетесь в этом многообразии замечательных новых фич и, как следствие, затрудняетесь сходу определить, что из этого всего вам действительно стоило бы взять на вооружение в своей повседневной работе по написанию кода. Не стоит расстраиваться, в этой статье вашему вниманию будут представлены 21 новая фича современного C++, которые помогут сделать ваш проект лучше, а работу над ним легче. Сообщество C++ дополняет стандарт чаще, чем Apple выпускает новые iPhone. Благодаря этому C++ теперь больше похож на большого слона, а съесть целого слона за один присест невозможно. Вот почему я решил написать эту статью, чтобы дать вашему путешествию по современному C++ своего рода отправную точку. Моя целевая аудитория здесь — люди, которые переходят со старого (т.е. 98/03) С++ на современный (т.е. 2011 и далее) С++. Я отобрал ряд фич современного C++ и постарался объяснить их на лаконичных примерах, чтобы вы научились определять места, где их можно использовать. Раньше вам нужно было считать цифры или нули, но, начиная с C++14, вы можете сделать большие числа намного нагляднее. Эта фича помогает облегчить навигацию по словам и цифрам. Или, допустим, вы можете повысить читаемость номера кредитной карты или социального страхования. Благодаря сгруппированным разрядам, ваш код станет немного выразительнее. Семантически похоже на использование typedef, однако псевдонимы типов легче читаются и совместимы с шаблонами С++. Поблагодарите С++11. По большей части это будут какие-нибудь реальные единицы, такие как kb, mb, км, см, рубли, доллары, евро и т.д. Пользовательские литералы позволяют вам не определять функции, для выполнения преобразования единиц измерения во время выполнения, а работать с ним как с другими примитивными типами. Очень удобно для единиц и измерения. Благодаря добавлению constexpr вы можете добиться нулевого влияния на производительность во время выполнения, что мы увидим позже в этой статье, и более подробно вы можете почитать об этом в другой статье, которую я написал, — “Использование const и constexpr в С++”. Раньше вам нужно было инициализировать поля их значениями по умолчанию в конструкторе или в списке инициализации. Но начиная с C++11 можно задавать обычным переменным-членам класса (тем, которые не объявлены с ключевым словом static) инициализирующее значение по умолчанию, как показано ниже: Это особенно полезно, когда в качестве полей выступают сразу несколько вложенных объектов, определенных, как показано ниже: В этом случае вам не нужно инициализировать их в списке инициализации. Вместо этого вы можете напрямую указать значение по умолчанию во время объявления. Вы также можете инициализировать во время объявления const static члены класса, как показано выше. Присваивайте значения контейнерам непосредственно с помощью списка инициализаторов, как это можно делать с C-массивами. Это справедливо и для вложенных контейнеров. Скажите спасибо С++11. auto-типизированные переменные выводятся компилятором на основе типа их инициализатора. Чрезвычайно полезно с точки зрения удобочитаемости, особенно для сложных типов: Функции также могут выводить тип возвращаемого значения с помощью auto. В C++11 тип возвращаемого значения должен быть указан либо явно, либо с помощью decltype, например: Приведенная выше форма определения возвращаемого типа называется trailing return type, т.е. -> return-type. Синтаксический сахар для перебора элементов контейнера. Обратите внимание на разницу при использовании int в противовес int&: C++11 добавляет в язык новые умные указатели: std::unique_ptr, std::shared_ptr, std::weak_ptr. А std::auto_ptr устарел, и в конечном итоге удален в C++17. Гайдлайны ISO CPP рекомендуют избегать явных вызовов new и delete, выразив это в правиле “никаких голых new”. Я уже писал об этом в статье “Разбираемся с unique_ptr в С++ на примерах”. C++11 добавил новый тип пустого указателя, предназначенный для замены макроса C NULL. nullptr имеет тип std::nullptr_t и может быть неявно преобразован в типы непустых указателей, и в отличие от NULL, не конвертируем в целочисленные типы, за исключением bool. Типобезопасные перечисления, которые решают множество проблем с C-перечислениями, включая неявные преобразования, арифметические операции, невозможность указать базовый тип, загрязнение области видимости и т.д. Приведение в стиле C изменяет только тип, не затрагивая сами данные. В то время как старый C++ имел небольшой уклон в типобезопасность, он предоставлял фичу указания оператора/функции преобразования типа. Но это было неявное преобразование типов. Начиная с C++11, функции преобразования типов теперь можно сделать явными с помощью спецификатора explicit следующим образом: Если приведенный выше код кажется вам странным, то можете прочитать мой подробный разбор этой темы — “Приведение типов в С++”. Когда объект будет уничтожен или не будет более использоваться после выполнения выражения, целесообразнее переместить (move) ресурс, а не копировать его. Копирование включает в себя ненужные накладные расходы, такие как выделение памяти, высвобождение и копирование содержимого памяти и т.д. Рассмотрим следующую функцию, меняющую местами два значения: Использование move позволяет вам напрямую обменивать ресурсы вместо их копирования: А теперь представьте, что происходит, когда Т это, скажем, vector<int> размера n. И n достаточно велико. В первой версии вы читаете и записываете 3*n элементов, во второй версии вы в по сути читаете и записываете только 3 указателя на буферы векторов плюс 3 размера буферов. Конечно, класс Т должен знать, как ему перемещаться; ваш класс должен иметь оператор присваивания перемещением и конструктор перемещения для класса Т, чтобы это работало. Эта фича даст вам значительный прирост в производительности — именно то, поэтому люди используют C++ (т.е., чтобы выжать последние 2-3 капли скорости). В официальной терминологии известные как forwarding references (передаваемые ссылки). Универсальная ссылка объявляется с помощью синтаксиса Т&&, где Т является шаблонным параметром типа, или с помощью auto&&. Они в свою очередь служат фундаментом для двух других крупных фич: move-семантика И perfect forwarding, возможность передавать аргументы, которые являются либо lvalue, либо rvalue. Универсальные ссылки позволяют ссылаться на привязку либо к lvalue, либо к rvalue в зависимости от типа. Универсальные ссылки следуют правилам свертывания ссылок: T& & становится  T&   T& && становится T& T&& & становится T& T&& && становится T&& Вывод шаблонного параметра типа с lvalue ​​и rvalue: Если вам это кажется сложным и странным, тогда для начала прочитайте это, а затем возвращайся обратно. Синтаксис ... создает пакет параметров или расширяет уже существующий. Шаблонный пакет параметров — это шаблонный параметр, который принимает ноль или более аргументов-шаблонов (нетипизированных объектов, типов или шаблонов). Шаблон С++ с хотя бы одним пакетом параметров называется вариативный шаблоном с переменным количеством аргументов (variadic template). Константные выражения — это выражения, вычисляемые компилятором во время компиляции. В приведенном выше примере функция fibonacci выполняется/вычисляется компилятором во время компиляции, и будет заменена на результат в вызове места. Я написал подробную статью, раскрывающую эту тему, “Использование const и constexpr в С++”. У вас вполне закономерно может возникнуть вопрос, зачем вам писать 8+ букв (т.е. = default;), когда можно просто использовать {}, т.е. пустой конструктор? Никто вас не останавливает. Но подумай о конструкторе копирования, операторе копирования присваиванием, и т.д. Пустой конструктор копирования, например, не то же самое, что конструктор копирования по умолчанию (который будет выполнять почленную копию всех членов). Вы можете ограничить определенную операцию или способ инстанцирования объекта, просто удалив соответствующий метод, как показано ниже: В старом С++ вы должны были сделать его приватным. Но теперь в вашем распоряжении есть директива компилятора delete. В старом C++ вам нужно создавать функцию-член для  инициализации и вызывать ее из всех конструкторов для достижения универсально инициализации. Но начиная с C++11 конструкторы теперь могут вызывать другие конструкторы из того же класса с помощью списка инициализаторов. Я думаю, что эта фича не нуждается в представлении и является фаворитом среди других фич. Теперь вы можете объявлять функции где угодно. И это не будет стоить вам никаких дополнительных накладных расходов.  Я написал отдельную статью на эту тему — “Разбираемся с лямбда-выражениями в C++ на примерах”. В более ранних версиях C++ инициализатор либо объявлялся перед оператором и просачивался во внешнюю область видимости, либо использовалась явная область видимости. В C++17 появилась новая форма if/switch, которую можно записать более компактно, а улучшенный контроль области видимости делает некоторые ранее подверженные ошибкам конструкции немного более надежными: Как это работает Кортежи представляют собой набор разнородных значений фиксированного размера. Доступ к элементам std::tuple производится с помощью std::tie или std::get. Вы также можете выхватывать произвольные и разнородные возвращаемые значения следующим образом: Используйте std::ignore в качестве плейсхолдера для игнорируемых значений. В С++ 17, вместо этого следует использовать структурированные привязки. Автоматическое выведение аргументов шаблона очень похоже на то, как это делается для функций, но теперь также включает и конструкторы классов. Здесь мы только слегка коснулись огромного набора новых фич и возможности их применения. В современном C++ можно найти еще очень много чего, но тем не менее вы можете считать этот набор хорошей отправной точкой. Современный C++ расширяется не только с точки зрения синтаксиса, но также добавляется гораздо больше других функций, таких как неупорядоченные контейнеры, потоки, регулярное выражение, Chrono, генератор/распределитель случайных чисел, обработка исключений и множество новых алгоритмов STL (например, all_of(), any_of(), none_of(), и т.д). Да пребудет с вами C++! Завтра вечером пройдет открытое занятие, посвященное Boost. На уроке вы узнаете, как подключать Boost в проект с помощью cmake; познакомитесь подробнее с библиотеками Boost и научитесь их использовать. Записаться на урок можно на странице курса "C++ Developer. Professional".