Обновления C++ подборка изменений из трех стандартов языка


Оглавление (нажмите, чтобы открыть):

Обновления C++: подборка изменений из трех стандартов языка

9’158 подписчиков
2’915 просмотров на пост

Полезные материалы по всему, что может быть полезно плюсовику/сишнику.

Детальная рекламная статистика будет доступна после прохождения простой процедуры регистрации

  • Детальная аналитика 70’046 каналов
  • Доступ к 28’004’146 рекламных постов
  • Поиск по 112’332’059 постам
  • Отдача с каждой купленной рекламы
  • Графики динамики изменения показателей канала
  • Где и как размещался канал
  • Детальная статистика по подпискам и отпискам

Найдено 675 постов

Создатель C++, Бьёрн Страуструп, в статье рассказывает о том, какие есть проблемы с программированием.

Все мы любим и часто пользуемся стандартной библиотекой C++ STL. Однако есть случаи, когда это невыгодно для производительности. В этой статье как раз и рассматриваются такие случаи.

Docker — популярная на сегодняшний день технология контейнеризации вашего окружения. Если вы хотите идти в ногу со временем, вам надо начать использовать Docker. Это просто. В этой статье рассказывается, как настроить Docker для работы с С++ в Visual Studio Code.

GitHub Actions: что это и как использовать

Процесс разработки кода может быть утомительным. Но что если бы вы могли снять часть этой работы с ваших плеч? В этом поможет GitHub Actions.

Интересная статья с хорошими аргументами в пользу того, почему язык С — не язык низкого уровня.

С++ — один из самых сложных, интересных и разносторонних языков программирования. В настоящем руководстве вы изучите основы языка на множествах примерах и задач. Так, например, вы познакомитесь с объектно-ориентированным и многопоточным программированием, научитесь работать с памятью и применять различные алгоритмы для решения задач.

Entity-component-system — это архитектурный шаблон, который в основном используется в разработке игр. ECS следует за композицией по принципу наследования, что позволяет повысить гибкость в определении объектов, где каждый объект в сцене игры является сущностью. В этой статье напишем простую реализацию Entity Component System средствами C++.

Мы с вами ещё не создавали игры. Или создавали, но не такие популярные и интересные, как марио. В этом видео мы это исправим.

Если вас интересует там блокчейна и по чистой случайности вы ещё и пишете на C++, следующая серия лекций именно для вас.

13 ресурсов, чтобы выучить математику

Среди разработчиков часто возникают споры о том, необходимо ли изучать математику. Если вас мучает ее незнание, то скорее читайте нашу статью.

Какая сортировка самая быстрая? Тестируем алгоритмы

На собеседованиях часто спрашивают, какая сортировка самая быстрая. Вопрос с подвохом. Объясняем, почему, и ищем оптимальный вариант.

Cryptopp — это библиотека для шифрования данных и отображения многочисленных криптографических алгоритмов.

В данном курсе вы познакомитесь с фреймворком C++ для создания графических приложений Qt.

Подход автора к изучению С++ можно считать самым эффективным — в процессе чтения вы изучаете язык на лучших примерах решения задач, обзоре стандартных ошибок и упражнениях. Вы научитесь не языку, по словам автора, а решать с помощью него проблемы программирования, так что десятое издание — подтверждение успешности его подхода к обучению.

Небольшой список популярных уже реализованных алгоритмов в языке C++. Так, например, вы познакомитесь с функцией swap, reverse, rotate, generate и многие другие.

Указатели — одна из наиболее сложных и, одновременно, особенно важных тем программирования на языках C/C++. Поскольку область применения этих языков находится между системным программированием и созданием высоконагруженных приложений, правильное управление памятью является главным требованием к профессиональному разработчику. Данное руководство с лихвой покроет весь нужный объём знаний, чтобы вы приступили к активному использованию указателей в своей практике.

Если вы хотите увеличить скорость работы ваших программ, используйте паралллельные алгоритмы стандартной библиотеки C++ 17. Данная же статья познакомит вас с самыми основными из них.

Assembler, виртуализация, Unix-система, Си, работа с памятью — это всё, что вас ждёт в следующей статье, в которой автор пишет собственную виртуальную машину.

В этом видео поговорим, в чём же разница между указателями и ссылками.

Классика программирования, руководство Стивена Кочана является одним из лучших по изучению языка Си для начинающих. В то же время, автор на множестве хороших примеров задач постарался охватить больше общие темы, чтобы у вас не было трудностей с изучением других Си-подобных языков вроде C++, Objective-C или C# в будущем.

Где взять стандарт C++?

Многие ответы ссылаются на стандарт С++, а где его взять?

2 ответа 2

Стандарт С++ называется International Standard ISO/IEC 14882:2020(E) – Programming Language C++.

Число после двоеточия — год издания, предыдущие стандарты были в 2014, 2011, 2003 и 1998 годах.

Официальное издание стандарта

Официальное издание стандарта публикуется ISO, и стоит денег. Его можно купить на ansi.org.

Также почитать/скачать можно здесь.

Черновики стандарта

На сайте open-std.org публикуются рабочие документы комитета про стандартизации, в том числе и черновики стандарта.
Когда очередная версия стандарта готова, публикуется «финальный черновик» (Final Draft), который затем отправляется в ISO. Он практически ничем не отличается от официального издания стандарта. Однако после публикации официального издания доступ к финальному черновику закрывается.

PDF файл рабочего черновика

Последняя опубликованная версия черновика:
N4820 от 2020-06-18.

Репозиторий GitHub

«Исходники стандарта» размещены на GitHub — https://github.com/cplusplus/draft
Их можно скомпилировать в .pdf и получить самый свежий черновик.

Финальные черновики

В этом ответе на английском StackOverflow есть обновляемый список черновиков стандарта, близких к С++14, С++11 и другим изданиям. Доступ к финальным черновикам закрыт, но их можно найти в интернете по их номерам.

Версии черновиков близких к официальным изданиям стандарта:

Также, постоянно обновляемый черновик стандарта в виде HTML, можно найти здесь.

Стандарт C++ вышел на русском

Небезызвестный Евгений Зуев выполнил таки свое обещание и перевел Стандарт.
Книжка доступна только (настолько мне известно) здесь.
Цена кусается, мнения у всех по этому вопросу разные. Смотрите сами.

Если кому интересно про Зуева, можете почитать его рассказ «Редкая профессия».
Сам я читал его давненько, понравилось.

07.11.2020, 11:49

Мануал на PG100 GSM Alarm System на русском (маны вообще на русском)
может кто знает где взять мануали на русском к сигнализациям

вышел в топ
в общем такое дело. раскручиваю сайт чисто для яндекса. прикупил недавно ссылок тематических.


Вышел за пределы
В MVS я первый раз пытаюсь рондомно вывести массив, но выводит ошибку и указывает на цикл, где я.

Вышел Debian 5.0.5
26-го июня, проект Debian объявил о выходе пятого по счёту крупного обновления стабильной ветки.

Вышел GCC 4.5.0
GNU и команда разработки GCC рады представить релиз GNU Compiler Collection версии 4.5. В новой.

/std (определение стандартной версии языка) /std (Specify Language Standard Version)

Включите поддерживаемые возможности языка C++ из указанной версии стандарта языка C++. Enable supported C++ language features from the specified version of the C++ language standard.

Синтаксис Syntax

Заметки Remarks

Параметр /std доступен в Visual Studio 2020 и более поздних версий. The /std option is available in Visual Studio 2020 and later. Он позволяет управлять тем, возможности какой версии языка программирования C++ в соответствии со стандартом ISO включены во время компиляции кода. It is used to control the version-specific ISO C++ programming language standard features enabled during compilation of your code. С помощью этого параметра можно отключать поддержку новых функций и библиотек, которые могут нарушить работу существующего кода, созданного в соответствии с определенной версией стандарта языка. This option allows you to disable support for certain new language and library features that may break your existing code that conforms to a particular version of the language standard. По умолчанию используется значение /std:c++14, которое отключает возможности языка и стандартных библиотек из последних версий языка C++. By default, /std:c++14 is specified, which disables language and standard library features found in later versions of the C++ language standard. Чтобы включить возможности и особенности стандарт C++17, используйте значение /std:c++17. Use /std:c++17 to enable C++17 standard-specific features and behavior. Чтобы явным образом включить реализованные возможности компилятора и стандартной библиотеки, предлагаемые для следующего проекта стандарта, используйте значение /std:c++latest. To explicitly enable the currently implemented compiler and standard library features proposed for the next draft standard, use /std:c++latest. Для всех функций C++ 20 требуется /std: C + + Latest; После завершения реализации будет включен новый параметр /std: c++ 20 . All C++20 features require /std:c++latest; when the implementation is complete, a new /std:c++20 option will be enabled.

Параметр по умолчанию /std:c++14 включает набор функций C++14, который реализован в компиляторе MSVC. The default /std:c++14 option enables the set of C++14 features implemented by the MSVC compiler. Этот параметр отключает компилятор и поддержку стандартной библиотеки для измененных и новых функций в более поздних версиях стандарта языка, за исключением некоторых функций C++17, уже реализованных в предыдущих выпусках компилятора MSVC. This option disables compiler and standard library support for features that are changed or new in more recent versions of the language standard, with the exception of some C++17 features already implemented in previous releases of the MSVC compiler. Во избежание критических изменений для пользователей, которые уже применяют функции, доступные начиная с Visual Studio 2015 с обновлением 2, следующие функции остаются включенными при указании параметра /std:c++14: To avoid breaking changes for users who have already taken dependencies on the features available as of Visual Studio 2015 Update 2, these features remain enabled when the /std:c++14 option is specified:

Дополнительные сведения о том, какие функции C++ 14 и C++ 17 включены при указании /std: c++ 14 , см. в разделе Примечания в таблице соответствия языков Майкрософт C++ . For additional information on which C++14 and C++17 features are enabled when /std:c++14 is specified, see the notes in Microsoft C++ language conformance table.

Параметр /std:c++17 включает полный набор функций C++17, который реализован в компиляторе MSVC. The /std:c++17 option enables the full set of C++17 features implemented by the MSVC compiler. Этот параметр отключает компилятор и поддержку стандартной библиотеки для измененных и новых функций в более поздних версиях рабочего проекта и исключает обновления C++ Standard после выхода C++17. This option disables compiler and standard library support for features that are changed or new in versions of the Working Draft and defect updates of the C++ Standard after C++17.

Параметр /std:c++latest включает возможности языка и библиотеки, появившиеся после версии C++17 и реализованные в настоящее время в компиляторе и библиотеках. The /std:c++latest option enables the post-C++17 language and library features currently implemented in the compiler and libraries. К ним могут относиться возможности из рабочего проекта C++20 и недействительные обновления стандарта C++, не включенные в C++17, а также экспериментальные предложения для проекта стандарта. These may include features from the C++20 Working Draft and defect updates of the C++ Standard that are not included in C++17, as well as experimental proposals for the draft standard. Список поддерживаемых возможностей языка и библиотек см. в статье Новые возможности Visual C++. For a list of supported language and library features, see What’s New for Visual C++. Параметр /std:c++latest не включает возможности, защищенные с помощью параметра /experimental, но может требоваться для их включения. The /std:c++latest option does not enable features guarded by the /experimental switch, but may be required to enable them.

Возможности компилятора и библиотек, включаемые параметром /std:c++latest, — это возможности, которые могут появиться в будущем стандарте C++, а также утвержденные возможности C++20. The compiler and library features enabled by /std:c++latest represent features that may appear in a future C++ standard, as well as C++20 features that are approved. Неутвержденные возможности предоставляются на условиях «как есть», могут удаляться без уведомления либо в них могут вноситься критические изменения. Features that have not been approved are subject to breaking changes or removal without notice and are provided on an as-is basis.

Параметр /std, действующий во время компиляции кода C++, можно обнаружить с помощью макроса препроцессора _MSVC_LANG. The /std option in effect during a C++ compilation can be detected by use of the _MSVC_LANG preprocessor macro. Дополнительные сведения см. в статье Макросы препроцессора. For more information, see Preprocessor Macros.

Параметры /std:c++14 и /std:c++latest доступны начиная с Visual Studio 2015 с обновлением 3. The /std:c++14 and /std:c++latest options are available beginning in Visual Studio 2015 Update 3. Параметр /std:c++17 доступен начиная с версии 15.3 Visual Studio 2020. The /std:c++17 option is available beginning in Visual Studio 2020 version 15.3. Как отмечалось выше, некоторые стандартные возможности C++17 включаются параметром /std:c++14, а остальные — параметром /std:c++17. As noted above, some C++17 standard behavior is enabled by the /std:c++14 option, but all other C++17 features are enabled by /std:c++17. Пока реализация стандарта C++20 не завершена, его возможности включаются параметром /std:latest. C++20 features are enabled by /std:latest until the implementation is complete.

В зависимости от версии или уровня обновления компилятора MSVC возможности C++17 при указании параметра /std:c++17 могут быть реализованы не полностью. Depending on the MSVC compiler version or update level, C++17 features may not be fully implemented or fully conformant when you specify the /std:c++17 options. Общие сведения о согласовании C++ языка в Visual C++ по версии выпуска см. в разделе Таблица C++ соответствия языков Майкрософт. For an overview of C++ language conformance in Visual C++ by release version, see Microsoft C++ language conformance table.

Установка данного параметра компилятора в среде разработки Visual Studio To set this compiler option in the Visual Studio development environment

Откройте диалоговое окно Страницы свойств проекта. Open the project’s Property Pages dialog box. Подробнее см. в статье Настройка компилятора C++ и свойства сборки в Visual Studio. For details, see Set C++ compiler and build properties in Visual Studio.

Выберите Свойства конфигурации, C/C++ , Язык. Select Configuration Properties, C/C++, Language.

В разделе Стандарт языка C++ выберите стандарт языка, который должен поддерживаться, в раскрывающемся списке, а затем нажмите кнопку ОК или Применить, чтобы сохранить изменения. In C++ Language Standard, choose the language standard to support from the dropdown control, then choose OK or Apply to save your changes.

Новые возможности С++17 и библиотеки STL

Функциональность языка C++ значительно расширилась с выходом C++11, C++14 и недавней версии C++17. На текущий момент он совсем не похож на себя образца десятилетней давности. Стандарт С++ упорядочивает не только язык, но и STL. В моем блоге на большом количестве примеров показаны наилучшие способы использования возможностей STL. Но для начала в текущей главе мы сконцентрируемся на самых важных особенностях языка. Изучив их, вы сможете писать легко читаемый, удобный в сопровождении и выразительный код.

Мы рассмотрим, как получить доступ к отдельным элементам пар, кортежей и структур с помощью структурированных привязок и ограничить область види­мости переменных благодаря новым возможностям по инициализации переменных внутри выражений if и switch . Синтаксические двусмысленности, появившиеся в C++11 из-за нового синтаксиса инициализатора с фигурными скобками, который выглядит так же, как синтаксис списков инициализаторов, были исправлены в новых правилах инициализатора с фигурными скобками. Точный тип экземпляра шаблонного класса может быть определен по аргументам, переданным его конструк­тору, а если разные специализации шаблонного класса выполняются в разном коде, то это легко выразить с помощью constexpr-if . Обработка переменного количества параметров в шаблонных функциях значительно упростилась благодаря новым выражениям свертки. Наконец, стало гораздо удобнее определять доступные глобально статические объекты в библиотеках, указанных в заголовочных файлах, благодаря новой возможности объявлять встраиваемые переменные, что ранее было выполнимо только для функций.

Мастер Йода рекомендует:  Скоро забронировать медийную рекламу в Яндексе можно будет только через новый интерфейс

Отдельные примеры данной главы могут оказаться более интересными для тех, кто реализует библиотеки, нежели для тех, кто пишет приложения. Для полноты картины мы рассмотрим несколько свойств.

Применяем структурированные привязки (декомпозицию) для распаковки набора возвращаемых значений

В C++17 появилась новая возможность, объединяющая синтаксический сахар и автоматическое определение типа, — структурированные привязки. Эта функция помогает присваивать отдельные значения пар, кортежей и структур отдельным переменным. В других языках программирования этот механизм называется распаковкой.

Как это делается

Применение декомпозиции для присвоения значений нескольким переменным на основе одной упакованной структуры всегда выполняется за один шаг. Сначала рассмотрим, как это делалось до появления С++17. Затем взглянем на несколько примеров, в которых показаны способы воплощения этого в С++17.

  1. Получаем доступ к отдельным значениям std:: pair . Представьте, что у нас есть математическая функция divide_remainder , которая принимает в качестве параметров делимое и делитель и возвращает частное и остаток в std::pair .

Рассмотрим следующий способ получения доступа к отдельным значениям полученной пары.

Вместо выполнения действий, показанных во фрагменте выше, мы теперь можем присвоить отдельные значения конкретным переменным с говорящими именами, что более удобочитаемо:

  1. Структурированные привязки работают и для std::tuple . Рассмотрим следу­ющий пример функции, которая возвращает информацию о ценах на акции:

Присваивание результата ее работы отдельным переменным выглядит так же, как и в предыдущем примере:

  1. Декомпозицию можно применять и для пользовательских структур. В качестве примера создадим следующую структуру.

Теперь можно получить доступ к ее членам с помощью декомпозиции. Мы даже можем сделать это в цикле, если предполагается наличие целого вектора таких структур:

Как это работает

Структурированные привязки всегда применяются по одному шаблону:

□ Количество переменных var1 , var2 . должно точно совпадать с количеством переменных в выражении, в отношении которого выполняется присваивание.

  • Элементом должен быть один из следующих объектов:
    • std::pair ;
    • std::tuple ;
    • структура. Все члены должны быть нестатическими и определенными в одном базовом классе. Первый объявленный член присваивается первой переменной, второй член — второй переменной и т. д.;
    • массив фиксированного размера.
  • Тип может иметь модификаторы auto, constauto, constauto& и даже auto&& .

Если в квадратных скобках вы укажете слишком мало или слишком много пере­менных, то компилятор выдаст ошибку.

В этом примере мы пытаемся поместить кортеж с тремя переменными всего в две переменные. Компилятор незамедлительно сообщает нам об ошибке:

Дополнительная информация

С помощью структурированных привязок вы точно так же можете получить доступ к большей части основных структур данных библиотеки STL. Рассмотрим, например, цикл, который выводит все элементы контейнера std::map :

Пример работает потому, что в момент итерации по контейнеру std::map мы получаем узлы std::pair на каждом шаге этого процесса. Именно эти узлы распаковываются с помощью структурированных при­вязок ( key_type представляет собой строку с именем species, а value_type — пере­менную count типа size_t ), что позволяет получить к ним доступ по отдельности в теле цикла.

До появления C++17 аналогичного эффекта можно было достичь с помощью std ::tie :

Здесь показано, как распаковать полученную пару в две переменные. Примене­ние контейнера std::tie не так удобно, как использование декомпозиции, ведь нам надо заранее объявить все переменные, которые мы хотим связать. С другой сто­роны, пример демонстрирует преимущество std::tie перед структурированными привязками: значение std::ignore играет роль переменной-пустышки. В данном случае частное нас не интересует и мы отбрасываем его, связав с std::ignore .

Когда мы применяем декомпозицию, у нас нет переменных-пустышек tie , поэтому нужно привязывать все значения к именованным переменным. Это может оказаться неэффективным, если позже не задействовать не­которые переменные, но тем не менее компилятор может оптимизировать неиспользованное связывание.

Раньше функцию divide_remainder можно было реализовать следующим об­разом, используя выходные параметры:

Получить к ним доступ можно так:

Многие все еще предпочитают делать именно так, а не возвращать пары, кор­тежи и структуры. При этом они приводят следующие аргументы: код работает быстрее , поскольку мы не создаем промежуточные копии этих значений. Но для современных компиляторов это неверно — они изначально оптимизированы так, что подобные копии не создаются.

Помимо того что аналогичной возможности нет в языке C, возврат сложных структур в качестве выходных параметров долгое время счи­тался медленным, поскольку объект сначала нужно инициализировать в возвращающей функции, а затем скопировать в переменную, которая должна будет содержать возвращаемое значение на вызывающей сторо­не. Современные компиляторы поддерживают оптимизацию возвраща­емых значений ( return value optimization , RVO ), что позволяет избежать создания промежуточных копий.

Ограничиваем область видимости переменных в выражениях if и switch

Максимальное ограничение области видимости переменных считается хорошим тоном. Иногда, однако, переменная должна получить какое-то значение, а потом нужно его проверить на соответствие тому или иному условию, чтобы продолжить выполнение программы. Для этих целей в С++17 была введена инициализация переменных в выражениях if и switch .


Как это делается

В данном примере мы воспользуемся новым синтаксисом в обоих контекстах, чтобы увидеть, насколько это улучшит код.

  • Выражение if . Допустим, нужно найти символ в таблице символов с помощью метода findконтейнера std::map :
  • Выражение switch . Так выглядит код получения символа из пользовательско­го ввода и его одновременная проверка в выражении switch для дальнейшего управления персонажем компьютерной игры:

Как это работает

Выражения if и switch с инициализаторами по сути являются синтаксическим сахаром. Два следующих фрагмента кода эквивалентны:

То же верно и для выражений switch.

Благодаря описанному механизму область видимости переменной остается минимальной. До С++17 этого можно было добиться только с помощью дополни­тельных фигурных скобок, как показано в соответствующих примерах. Короткие жизненные циклы уменьшают количество переменных в области видимости, что позволяет поддерживать чистоту кода и облегчает рефакторинг.

Дополнительная информация

Еще один интересный вариант — ограниченная область видимости критических секций. Рассмотрим следующий пример:

Сначала создается std::lock_guard . Этот класс принимает мьютекс в каче­стве аргумента конструктора. Он запирает мьютекс в конструкторе, а затем, когда выходит из области видимости, отпирает его в деструкторе. Таким об­разом, невозможно забыть отпереть мьютекс. До появления С++17 требовалась дополнительная пара скобок, чтобы определить область, где мьютекс снова откроется.

Не менее интересный пример — это область видимости слабых указателей. Рас­смотрим следующий фрагмент кода:

Это еще один пример с бесполезной переменной shared_pointer . Она попадает в текущую область видимости, несмотря на то что потенциально является бесполезной за пределами условного блока if или дополнительных скобок!

Выражения if с инициализаторами особенно хороши при работе с устаревшими API, имеющими выходные параметры:

GetExitCodeProcess — функция API ядра Windows. Она возвращает код для заданного дескриптора процесса, но только в том случае, если данный дескриптор корректен. После того как мы покинем этот условный блок, переменная станет бесполезной, поэтому она не нужна в нашей области видимости.

Возможность инициализировать переменные внутри блоков if, очевидно, очень полезна во многих ситуациях, особенно при работе с устаревшими API, которые используют выходные параметры.

Всегда ограничивайте области видимости с помощью инициализации в выражениях if и switch. Это позволит сделать код более компактным, простым для чтения, а в случае рефакторинга его будет проще перемещать.

Новые правила инициализатора с фигурными скобками

В C++11 появился новый синтаксис инициализатора с фигурными скобками <> . Он предназначен как для агрегатной инициализации, так и для вызова обычного конструктора. К сожалению, когда вы объединяли данный синтаксис с типом пере­менных auto , был высок шанс выразить не то, что вам нужно. В C++17 появился улучшенный набор правил инициализатора. В следующем примере вы увидите, как грамотно инициализировать переменные в С++17 и какой синтаксис при этом использовать.

Как это делается

Переменные инициализируются в один прием. При использовании синтаксиса инициализатора могут возникнуть две разные ситуации.

  1. Применение синтаксиса инициализатора с фигурными скобками без выведения типа auto :
  1. Использование синтаксиса инициализатора с фигурными скобками с выведе­нием типа auto :

Как это работает

Отдельно от механизма выведения типа auto оператор <> ведет себя предсказуемо, по крайней мере при инициализации обычных типов. При инициализации кон­тейнеров наподобие std::vector , std::list и т. д. инициализатор с фигурными скобками будет соответствовать конструктору std:: initializer_list этого класса контейнера. При этом он не может соответствовать неагрегированным конструкто­рам (таковыми являются обычные конструкторы, в отличие от тех, что принимают список инициализаторов).

std::vector , например, предоставляет конкретный неагрегированный кон­структор, заносящий в некоторое количество элементов одно и то же значение: std::vector v (N, value) . При записи std::vector v

выбирается конструктор initializer_list , инициализирующий вектор с двумя элементами: N и value. Об этом следует помнить.

Есть интересное различие между оператором <> и вызовом конструктора с по­мощью обычных скобок () . В первом случае не выполняется неявных преобразова­ний типа: int x (1.2) ; и int x = 1.2 ; инициализируют переменную x значением 1, округлив в нижнюю сторону число с плавающей точкой и преобразовав его к типу int . А вот выражение int x <1.2>; не скомпилируется, поскольку должно точно соответствовать типу конструктора.

Кто-то может поспорить о том, какой стиль инициализации является лучшим. Любители стиля с фигурными скобками говорят, что последние делают процесс явным, переменная инициализируется при вызове конструктора и эта строка кода ничего не инициализирует повторно. Более того, при использовании фигурных скобок <> будет выбран единственный подходящий конструктор, в то время как в момент применения обычных скобок () — ближайший похожий конструктор, а также выполнится пре­образование типов.

Дополнительное правило, включенное в С++17, касается инициализации с выведением типа auto: несмотря на то что в C++11 тип переменной auto x <123>; ( std::initializer_list с одним элементом) будет определен кор­ректно, скорее всего, это не тот тип, который нужен. В С++17 та же переменная будет типа int .

  • в конструкции autovar_name ; переменная var_nameбудет иметь тот же тип, что и one_element;
  • конструкция autovar_name ; недействительна и не будет скомпилирована;
  • конструкция autovar_name= ; будет иметь тип std::initializer_list , где T — тип всех элементов списка.

В С++17 гораздо сложнее случайно определить список инициализаторов.

Попытка скомпилировать эти примеры в разных компиляторах в режи­ме C++11 или C++14 покажет, что одни компиляторы автоматически выводят тип auto x <123>; как int , а другие — как std::initializer_list . Подобный код может вызвать проблемы с переносимостью!

Разрешаем конструктору автоматически выводить полученный тип класса шаблона

Многие классы C++ обычно специализируются по типам, о чем легко догадаться по типам переменных, которые пользователь задействует при вызовах конструктора. Тем не менее до С++17 эти возможности не были стандартизированы. С++17 позво­ляет компилятору автоматически вывести типы шаблонов из вызовов конструктора.

Как это делается

Данную особенность очень удобно проиллюстрировать на примере создания экзем­пляров типа std::pair и std::tuple . Это можно сделать за один шаг:

Как это работает

Определим класс-пример, где автоматическое выведение типа шаблона будет вы­полняться на основе переданных значений:

О’кей, это всего лишь еще один класс шаблона. Вот как мы раньше создавали его объект (инстанцировали шаблон):

Теперь же можно опустить специализацию шаблона:

До появления C++17 это было возможно только при реализации вспомогатель­ной функции:

Используя подобные вспомогательные функции, можно было добиться такого же эффекта:

STL предоставляет множество аналогичных инструментов: std::make_ shared , std::make_unique , std::make_tuple и т. д. В C++17 эти функции могут считаться устаревшими. Но, конечно, они все еще будут работать для обеспечения обратной совместимости.

Дополнительная информация

Из данного примера мы узнали о неявном выведении типа шаблона. Однако в не­которых случаях на этот способ нельзя полагаться. Рассмотрим следующий класс­пример:

Эта структура, sum, принимает произвольное количество параметров и сумми­рует их с помощью выражений свертки (пример, связанный с выражениями свертки, мы рассмотрим далее в этой главе). Полученная сумма сохраняется в переменную-член value . Теперь вопрос заключается в том, что за тип — T? Если мы не хотим указывать его явно, то ему следует зависеть от типов значений, переданных в кон­структор. В случае передачи объектов-строк тип должен быть std: :string . При передаче целых чисел тип должен быть int . Если мы передадим целые числа, числа с плавающей точкой и числа с удвоенной точностью, то компилятору следует опре­делить, какой тип подходит всем значениям без потери точности. Для этого мы предоставляем явные правила выведения типов:

Согласно этим правилам компилятор может использовать типаж std::common_ type_t , который способен определить, какой тип данных подходит всем значениям. Посмотрим, как его применить:

В первой строке мы создаем объект типа sum на основе аргументов конструкто­ра, имеющих типы unsigned , double , int и float . Типаж std:: common_type_t возвра­щает тип double , поэтому мы получаем объект типа sum . Во второй строке мы предоставляем экземпляр типа std::string и строку в стиле C. В соответствии с нашими правилами компилятор создает экземпляр типа sum .

При запуске этот код выведет значение 10 как результат сложения чисел и abcdef в качестве результата объединения строк.

Упрощаем принятие решений во время компиляции с помощью constexpr-if

В коде, содержащем шаблоны, зачастую необходимо по-разному выполнять опре­деленные действия в зависимости от типа, для которого конкретный шаблон был специализирован. В С++17 появились выражения constexpr-if , позволяющие значительно упростить написание кода в таких ситуациях.

Как это делается


В этом примере мы реализуем небольшой вспомогательный шаблонный класс. Он может работать с разными типами, поскольку способен выбирать различные пути выполнения кода в зависимости от типа, для которого мы конкретизируем шаблон.

    1. Напишем обобщенную часть кода. В нашем примере рассматривается простой класс, который добавляет значение типа Uк элементу типа Tс помощью функ­ции add :
  1. Представим, что тип T — это std::vector , а тип U — просто int . Каков смысл выражения «добавить целое число к вектору»? Допустим, нужно добавить данное число к каждому элементу вектора. Это делается в цикле:
  1. Следующий и последний шаг заключается в том, чтобы объединить оба вари­анта. Если T — это вектор, состоящий из элементов типа U , то выполняем цикл. В противном случае выполняем обычное сложение.
  1. Теперь класс можно использовать. Посмотрим, насколько хорошо он мо­жет работать с разными типами, такими как int , float , std::vector и std::vector :

Как это работает

Новая конструкция constexpr-if работает точно так же, как и обычные конструк­ции if-else . Разница между ними заключается в том, что значение условного выражения определяется во время компиляции. Весь код завершения, который компилятор сгенерирует из нашей программы, не будет содержать дополнительных ветвлений, относящихся к условиям constexpr-if . Кто-то может сказать, что эти механизмы работают так же, как и макросы препроцессора #if и #else , предназначенные для подстановки текста, но в данном случае всему коду даже не нужно быть синтаксически правильным. Ветвления конструкции constexpr-if должны быть синтаксически правильными, но неиспользованные ветви не обязаны быть семантически корректными.

В одном блоке constexpr-if-else может оказаться несколько условий (обра­тите внимание, что a и b должны зависеть от параметров шаблона, а не только от констант времени компиляции):

С помощью C++17 гораздо легче как выразить, так и прочитать код, получа­ющийся при метапрограммировании.

Дополнительная информация

Для того чтобы убедиться, каким прекрасным новшеством являются конструкции constexpr-if для C++, взглянем, как решалась та же самая задача до С++17:

Без конструкций constexpr-if этот класс работает для всех необходимых нам типов, но кажется очень сложным. Как же он работает?

Сами реализации двух разных функций add выглядят просто. Все усложняет объявление возвращаемого типа — выражение наподобие std::enable_if_t обращается в тип, если выполняется условие. В противном случае вы­ражение std::enable_if_t ни во что не обращается. Обычно такое положение дел считается ошибкой. Далее мы рассмотрим, почему в нашем случае это не так.

Для второй функции add то же условие используется противоположным обра­зом. Следовательно, условие может иметь значение true только для одной из двух реализаций в любой момент времени.

Когда компилятор видит разные шаблонные функции с одинаковым именем и должен выбрать одну из них, в ход вступает важный принцип: он обозначается аббревиатурой SFINAE, которая расшифровывается как Substitution Failure is not an ErrorСбой при подстановке — не ошибка»). В данном случае это значит, что компилятор не генерирует ошибку, если возвращаемое значение одной из функций нельзя вывести на основе неверного шаблонного выражения (то есть std::enable_if , когда условие имеет значение false ). Он просто продолжит работу и попробует обработать другие реализации функции. Вот и весь секрет.

Столько возни! Радует, что после выхода C++17 делать это стало гораздо проще.

Подключаем библиотеки с помощью встраиваемых переменных

Несмотря на то что в C++ всегда была возможность определить отдельные функ­ции как встраиваемые, C++17 дополнительно позволяет определять встраиваемые переменные. Это значительно упрощает реализацию библиотек, размещенных в за­головочных файлах, для чего раньше приходилось искать обходные пути.

Как это делается

В этом примере мы создаем класс-пример, который может служить членом ти­пичной библиотеки, размещенной в заголовочном файле. Мы хотим предоставить доступ к статическому полю класса через глобально доступный элемент класса и сделать это с помощью ключевого слова inline , что до появления C++17 было невозможно.

    1. Класс process_monitor должен содержать статический член и быть доступным глобально сам по себе, что приведет (при включении его в несколько единиц трансляции) к появлению символов, определенных дважды:
  1. Теперь при попытке включить данный код в несколько файлов с расширением .cpp , а затем скомпилировать и связать их произойдет сбой на этапе связывания. Чтобы это исправить, добавим ключевое слово inline :

Вуаля! Все работает!

Как это работает

Программы, написанные на C++, зачастую состоят из нескольких исходных фай­лов C++ (они имеют расширения .cpp или .cc ). Они отдельно компилируются в модули/объектные файлы (обычно с расширениями .o ). На последнем этапе все эти модули/объектные файлы компонуются в один исполняемый файл или разделяемую/статическую библиотеку.

На этапе связывания ошибкой считается ситуация, когда компоновщик встре­чает вхождение одного конкретного символа несколько раз. Предположим, у нас есть функция с сигнатурой int foo();. Если в двух модулях определены одинако­вые функции, то какую из них считать правильной? Компоновщик не может про­сто подбросить монетку. Точнее, может, но вряд ли хоть один программист сочтет такое поведение приемлемым.

Традиционный способ создания функций, доступных глобально, состоит в объявлении их в заголовочном файле, впоследствии включенном в любой мо­дуль С++, в котором их нужно вызвать. Эти функции будут определяться в от­дельных файлах модулей. Далее они связываются с теми модулями, которые должны использовать эти функции. Данный принцип также называется правилом одного определения (one definition rule, ODR). Взгляните на рис. 1, чтобы луч­ше понять это правило.

Однако будь это единственный способ решения задачи, нельзя было бы созда­вать библиотеки, размещенные в заголовочных файлах. Такие библиотеки очень удобны, поскольку их можно включить в любой файл программы С++ с помощью директивы #include , и они мгновенно станут доступны. Для использования же библиотек, размещенных не в заголовочных файлах, программист также должен адаптировать сценарии сборки так, чтобы компоновщик связал модули библиотек и файлы своих модулей. Это неудобно, особенно для библиотек, содержащих толь­ко очень короткие функции.

В таких случаях можно применить ключевое слово inline — оно позволяет в порядке исключения разрешить повторяющиеся определения одного символа в разных модулях. Если компоновщик находит несколько символов с одинаковой сигнатурой, но они объявлены встраиваемыми, то он выберет первый и будет считать, что остальные символы имеют такое же определение. На программиста возложена ответственность за то, чтобы все одинаковые встраиваемые символы были определены абсолютно идентично.

Рис. 1. Правило одного определения (one definition rule, ODR)

Что касается нашего примера, компоновщик найдет символ process_monitor::standard_string в каждом модуле, который включает файл foo_lib.hpp . Без ключевого слова inline он не будет знать, какой символ выбрать, так что прекратит работу и сообщит об ошибке. Это же верно и для символа global_process_monitor . Как же выбрать правильный символ?

При объявлении обоих символов с помощью ключевого слова inline компонов­щик просто примет первое вхождение символа и отбросит остальные.

До появления C++17 единственным явным способом сделать это было предостав­ление символа с помощью дополнительного файла модуля C++, что заставляло пользователей библиотеки включать данный файл на этапе компоновки.

Ключевое слово inline по традиции выполняет и другую задачу. Оно указывает компилятору, что он может избавиться от вызова функции, взяв ее реализа­цию и поместив в то место, из которого функция вызывается. Таким образом, вызывающий код содержит на один вызов функции меньше — считается, что такой код работает быстрее. Если функция очень короткая, то полученный ассемблерный код также будет короче (предполагается, что количество инструкций, которые выполняют вызов функции, сохранение и восстановление стека и т. д., превышает количество строк с полезной нагрузкой). Если же встраиваемая функция очень длинная, то размер бинарного файла увеличится, а это не ускоряет работу про­граммы. Поэтому компилятор будет использовать ключевое слово inline как подсказку и может избавиться от вызовов функций, встраивая их тело. Он даже может встроить отдельные функции, которые программист не объявлял встраи­ваемыми.

Дополнительная информация

Одним из способов решения такой задачи до появления C++17 было создание функции static, которая возвращает ссылку на объект static :

Подобным образом вы можете совершенно легально включить заголовочный файл в несколько модулей и при этом получать доступ к одному и тому же экзем­пляру отовсюду. Однако объект не создается немедленно при старте программы — это происходит только при первом вызове функции-геттера. В некоторых случаях это может оказаться проблемой. Представьте, будто нужно, чтобы конструктор статического объекта, доступного глобально, при запуске программы выполнял некую важную операцию (в точности как наш класс-пример), но мы не получаем желаемого из-за вызова геттера ближе к концу программы.

Проблему можно решить еще одним способом: сделав класс foo шаблонным и воспользовавшись преимуществами шаблонов.

В C++17 оба варианта становятся неактуальны.

Реализуем вспомогательные функции с помощью выражений свертки

Начиная с C++11, в языке появились пакеты параметров для шаблонов с перемен­ным количеством аргументов. Такие пакеты позволяют реализовывать функции, принимающие переменное количество параметров. Иногда эти параметры объ­единяются в одно выражение, чтобы на его основе можно было получить результат работы функции. Решение этой задачи значительно упростилось с выходом C++17, где появились выражения свертки.

Как это делается

Реализуем функцию, которая принимает переменное количество параметров и воз­вращает их сумму.

  1. Сначала определим ее сигнатуру:
  1. Теперь у нас есть пакет параметров ts, функция должна распаковать все параме­тры и просуммировать их с помощью выражения свертки. Допустим, мы хотим воспользоваться каким-нибудь оператором (в нашем случае + ) вместе с . чтобы применить его ко всем значениям пакета параметров. Для этого нужно взять выражение в скобки:


  1. Теперь можно вызвать функцию следующим образом:
  1. Она работает не только с целочисленными типами; можно вызвать ее для любого типа, реализующего оператор + , например std::string :

Как это работает

Только что мы написали код, в котором с помощью простой рекурсии бинарный оператор ( + ) применяется к заданным параметрам. Как правило, это называется сверткой. В C++17 появились выражения свертки, которые помогают выразить ту же идею и при этом писать меньше кода.

Подобное выражение называется унарной сверткой. C++17 позволяет приме­нять к пакетам параметров свертки следующие бинарные операторы: + , – , * , / , % , ^ , & , | , = , , > , , >> , += , –= , *= , /= , %= , ^= , &= , |= , , >>= , == , != , , >= , && , ||, , ,.* , –>* .

Кстати, в нашем примере кода неважно, какую использовать конструкцию, ( ts + . ) или (. + ts );. Они обе работают так, как нужно. Однако между ними есть разница, которая может иметь значение в других случаях: если многоточие . нахо­дится с правой стороны оператора, то такое выражение называется правой сверткой. Если же оно находится с левой стороны, то это левая свертка.

В нашем примере с суммой левая унарная свертка разворачивается в конструк­цию 1 + (2 + (3 + (4 + 5))) , а правая унарная свертка развернется в (((1 + 2) + + 3) + 4) + 5 . В зависимости от того, какой оператор используется, могут про­явиться нюансы. При добавлении новых чисел ничего не меняется.

Дополнительная информация

Если кто-то вызовет функцию sum() и не передаст в нее аргументы, то пакет па­раметров произвольной длины не будет содержать значений, которые могут быть свернуты. Для большинства операторов такая ситуация считается ошибкой (но для некоторых — нет, вы увидите это чуть позже). Далее нужно решить, генерировать ошибку или же вернуть конкретное значение. Очевидным решением будет вернуть значение 0 .

Это делается так:

Таким образом, вызов sum() возвращает значение 0 , а вызов sum(1, 2, 3) — зна­чение (1 + (2 + (3 + 0))) . Подобные свертки с начальным значением называются бинарными.

Кроме того, обе конструкции, (ts + . + 0) и (0 + . + ts) , работают как полагается, но такая бинарная свертка становится правой или левой соответственно. Взгляните на рис. 2.

Рис. 2. Бинарные свертки

При использовании бинарных сверток для решения такой задачи, когда ар­гументы отсутствуют, очень важны нейтральные элементы — в нашем случае сложение любого числа с нулем ничего не меняет, что делает 0 нейтральным элементом. Поэтому можно добавить 0 к любому выражению свертки с помощью
операторов + или -. Если пакет параметров пуст, это приведет к возврату функ­цией значения 0. С математической точки зрения это правильно. С точки зрения реализации нужно определить, что именно является правильным в зависимости от наших требований.

Тот же принцип применяется и к умножению. Здесь нейтральным элементом станет 1 :

Результат вызова product(2, 3) равен 6 , а результат вызова product() без па­раметров равен 1 .

В логических операторах И ( && ) и ИЛИ ( || ) появились встроенные нейтраль­ные элементы. Свертка пустого пакета параметров с оператором && заменяется на true , а свертка пустого пакета с оператором || — на false .

Еще один оператор, для которого определено значение по умолчанию, когда он используется для пустых пакетов параметров, — это оператор «запятая» ( , ), заменяемый на void() .

Давайте взглянем на другие вспомогательные функции, которые можно реали­зовать с помощью этих механизмов.

Соотнесение диапазонов и отдельных элементов

Как насчет функции, которая определяет, содержит ли диапазон хотя бы одно из значений, передаваемых в пакете параметров с переменной длиной:

Вспомогательная функция использует функцию std::count из библиотеки STL. Она принимает три параметра: первые два представляют собой начальный и конечный итераторы того или иного итерабельного промежутка, а третий параметр — это значение, с которым будут сравниваться все элементы промежутка. Метод std::count возвращает количество всех элементов внутри диапазона, равных третьему параметру.

В нашем выражении свертки мы всегда передаем в функцию std::count начальный и конечный итераторы одного диапазона параметров. Однако в ка­честве третьего параметра мы всякий раз отправляем один параметр из пакета.

В конечном счете функция складывает все результаты и возвращает их вызыва­ющей стороне.

Ее можно использовать следующим образом:

Как видите, вспомогательная функция matches довольно гибкая — ее можно вызвать для векторов или даже строк. Она также будет работать для списка инициализаторов, контейнеров std::list , std::array , std::set и прочих!

Проверка успешности вставки нескольких элементов в множество

Напишем вспомогательную функцию, которая добавляет произвольное количество параметров в контейнер std::set и возвращает булево значение, показывающее, успешно ли прошла операция:

Как же это работает? Функция insert контейнера std::set имеет следующую сигнатуру:

Документация гласит, что при попытке вставить элемент функция insert вернет пару из iterator и переменной bool . Если вставка пройдет успешно, зна­чение переменной будет равно true . Итератор же в этом случае укажет на новый элемент множества, а в противном случае — на существующий элемент, который помешал вставке.

Наша вспомогательная функция после вставки обращается к полю .second . Оно содержит переменную bool , которая показывает, была ли вставка успешной. Если все полученные пары имеют значение true , то все вставки прошли успешно. Свертка объединяет все результаты вставки с помощью оператора && и возвращает результат.

Контейнер можно использовать следующим образом:

Обратите внимание: если мы попробуем вставить, например, три элемента, но в процессе окажется, что второй элемент вставить нельзя, свертка && . досрочно прекратит работать и оставшиеся элементы не будут добавлены:

Проверка попадания всех параметров в заданный диапазон

Поскольку можно убедиться, что одна из переменных находится в конкретном диапазоне, можно сделать то же самое для нескольких переменных с помощью выражений свертки:

Выражение (min определяет, находится ли каждый элемент пакета параметров в диапазоне между min и max (включая min и max ). Мы выбрали оператор && , чтобы свести все результаты булева типа к одному, который имеет значение true только в том случае, если все отдельные результаты имеют такое же значение.

Это работает следующим образом:

Что интересно: эта функция очень гибкая, поскольку единственным требова­нием, которое она предъявляет к типам, служит возможность сравнения экзем­пляров с помощью оператора . Это требование выполняется, например, типом std::string :

Отправка нескольких элементов в вектор

Кроме того, вы можете написать вспомогательную функцию, которая не обобщает никаких результатов, но обрабатывает несколько действий одного вида. Такими действиями могут быть вставки элементов в контейнер std: :vector , поскольку они не возвращают никаких результатов (функция std: :vector: :insert() сообщает об ошибке, генерируя исключения):

Стандарты языка С++

История стандартов языка C++

Важную роль в развитии языка C++ играют стандарты языка. Хотя язык C++ разрабатывался с 1980-х годов, первый стандарт языка C++98 был окончательно утвержден только в 1998 году.

В 2003 году был издан стандарт С++03, являющийся уточнением стандарта C++98.

Наиболее существенные изменения языка произошли в стандарте C++11, разработка которого завершилась в 2011 году. Далее будут изложен ряд нововведений стандарта C++11.

В 2014 году был издан стандарт С++14, не содержащий существенных изменений, а только устраняющий ряд дефектов стандарта С++11.

Разработка следующего стандарта C++17 будет завершена в 2020 году. Этот стандарт также содержит ряд интересных нововведений, однако их не столь много, как в стандарте С++11.

Ссылки:

Auto-тип переменной

При работе с контейнерами STL иногда приходится писать конструкции вида

map >::reverse_iterator it;

То есть описание типа может быть очень длинной строкой. Вместо этого в C++11 можно объявить имя типа, как auto , что означает, что компилятор должен сам определить тип переменной. В этом случае переменная должна быть явно инициализирована при объявлении, например:

auto it = a.begin();

В этом случае компилятор знает тип значения a , поэтому он может определить тип, который возвращает метод begin() , и тем самым будет определен тип переменной it .

Самый простой вариант (можно использовать для определения того, включена ли поддержка C++11):

В этом случае переменная a будет иметь тип int . Если написать:


то переменная a будет иметь тип double .

Обратите внимание, это НЕ динамическая типизация! Переменная по-прежнему имеет строго определенный тип, который не может быть впоследствии изменен! Но этот тип программист просто не указывает явно для облегчения труда.

range-based циклы

В C++11 появились range-based циклы: циклы, в которых переменная пробегает по всем значением контейнера. Контейнер должен поддерживать методы begin() и end() — это может быть vector , list , set , map .

Пример такого цикла для вывода элементов вектора:

vector a;
for (int elem: a)
cout auto .

Таким образом можно модифицировать элементы вектора, если сделать цикл по переменной-ссылке, а не по переменной-значению:

Если цикл пробегает по элементам контейнера map, то переменная будет парой из двух элементов: ключ и значение. То есть:

map a;
for (auto & elem: a)
<
// a.first — ключ элемента словаря, a.second — его значение
>

Универсальная инициализация

В языке C элементы массива можно инициализировать списком значений, например, так:

В С++11 такая универсальная инициализация распространена на структуры и классы. В списке инициализации в фигурных скобках необходимо указать значения полей структуры в том порядке, в котором они объявлены. Например,

В этом случае P.x будет равно 1, P.y будет 2.

Еще один способ использования универсальной инициализации:

Поскольку pair является структурой, то этот способ можно использовать для инициализации pair . Например, если функция возвращает значение типа pair , то можно написать:

Шаблоны с переменным числом аргументов и std::tuple

В C++11 появилась возможность создания шаблонов с переменным числом аргументов. Один из примеров такого шаблона — tuple, определенный в одноименном заголовочном файле. Это кортеж, то есть переменная, которая содержит несколько полей различных типов.

Доступ к составным полям tuple осуществляется при помощи функции-шаблона get с одним числовым параметром — номер поля, к которому производится доступ. Например:

get (person) = «Peter»;
get (person) = «Ivanov»;
get (person) = 16;

Заметим, что инстанцирование шаблона происходит при компиляции, то есть порядковый номер поля должен быть константой, определенной на момент компиляции (нельзя сделать цикл по номеру поля, т.е. нельзя, например, написать

Объекты класса tuple сравниваются в лексикографическом порядке, поэтому их удобно использовать при сортировке векторов вместо pair, если нужно сортировать по нескольким параметрам.

Структура tie

tie является структурой, определенной в заголовочном файле tuple . Структура tie похожа на tuple , только членами структуры являются ссылки на какие-либо переменные (или прочие lvalue, например, элементы массива и т.д.). Это позволяет модифицировать переменные, переданные в качестве параметров при создании tie.

Пример. Пусть функция f возвращает пару значений, то есть структуру pair или tuple. Хочется записать эти значения в две переменные. Раньше мы писали так:

auto res = f();
a = res.first;
b = res.second;

С использованием tie это можно сделать так:

Лямбда-функции

Лямбда-функции — это безымянные функции, которые не получают собственного имени, а используются только в месте объявления.

Распространенное использование лямбда-функций — это параметр-компаратор при сортировке. Вместо создания специальной функции-компаратора, можно описать функцию прямо в вызове функции sort.

Пример — отсортируем vector a по последней цифре числа:

То есть NULL является значением типа int , что может привести к проблемам сравнения указателя с целым числом. В стандарте C++11 для обозначения нулевого указателя появилось новое специальное значение nullptr .

Угловые скобки во вложенных шаблонах

При определении вложенных шаблонов, например:

в C++11 можно не ставить пробел между » long long int , который раньше был расширением GNU C++.

Кроме того, появились типы данных фиксированного размера, например, «32-битное целое число», они определены в заголовочном файле cstdint.

Стандартная библиотека

Много изменений произошло в стандартной библиотеке. Например, появились хеш-таблицы unordered_set и unordered_map. Появились усовершенствованные генераторы случайных чисел и поддержка регулярных выражений.

Как восстановить знания по C++ на сегодняшний день?

Сейчас программирую только под .Net C#

Последний раз программировал на С++ лет 6 назад (Visual C++ под VS 2005 + Qt)
Хочу вернуть знания, на сегодняшний день (конец 2014 года).
Помню только синтаксис, общие правила. Базовые знания в общем.

вопросы:
1. какая версия сейчас самая актуальная? не обязательно последняя, а востребованная скорее в производстве.
2. какую среду разработки взять? VS 2013 ? (ОС Windows 7-8)
3. С++ Boost — что это такое и стоит ли сразу переучиваться под него писать?
4. какие еще улучшения появились для разработчика? Может библиотеки какие, без которых не обходиться не один уважающий себя С++ программист ?
5. в каком направлении идти? (планирую в mobile-development мигрировать)

  • Вопрос задан более трёх лет назад
  • 7824 просмотра

0. Прочитайте Страуструпа последнее издание (англ). Если язык вы знали то это лучшая книга чтобы обновить знания

1. C++11 C++14, в производстве чаще пока еще С++03
2. Лучший компилятор clang++ (поддерживает любой стандарт и любую платформу)
3. boost это набор библиотек на все случаи жизни самый хорошо сынжинереный. Стоит писать не под него а с использованием
4. пункт 3
5. C++ для задач требующих точного понимания стоимости каждой операции, это embedded DSP Server computing
Math много чего еще

Учтите, С++ это инструмент который нужно учить постоянно

Дополню
————
С++ мультипарадигменный
А так же много уровней абстракции поддерживает

На нем можно писать как на чистом С — это самый низкий уровень
Можно ООП и абстракции
Можно паттерны
А можно функциональный стиль

С С++ в этом и проблема что знать нужно очень много.

Я могу сказать что я читал на протяжении своей карьеры
Прежде всего я умел программировать и имел представление об алгоритмах и модели памяти
(что такое указатели, алокация удаление и т д)

Сначала я почитал какую-то левую книжку и изучил его на уровне новичка. Написал проект библиотека линейной алгебры (это было еще в универе, задача была актуальна для меня) В принципе здесь моих знаний хватало чтобы стать джуниором, но я такой подход не люблю

Потом Страуструп (тогда это было издание по стандарту 03). Здесь я дополнил свои знания деталями которые пропустил при самостоятельном изучении. Тут стоит отметить что Страуструп весьма странная книга и написана тяжело. Секцию же ООП вообще там лучше не читать (самая последняя).

Где-то рядом я прочитал Гради Буч — ООА и ООП с примерами применения. Очень хорошая кика чтобы понять к чему все это придумали вообще. Сильно выправляла мозги

Потом был Керниган и Ричи Язык программирования С. Эту я прочитал просто от безделья, но теперь считаю что это было необходимо. Здесь можно понять зачем придумали именно С. И насколько там все просто, задумано.

Следом пошли техники: Герб Саттер Решение сложных задач на С++ и Новые сложные задачи С++
читать обязательно, разобрано много костылей и проблем языка. Дано очень много дельных советов

С Мейерс — Эффективное использование С++ туда же. Прекрасный разбор.

Макконнел — Совершенный код. Очень крутая книга. Она отшлифует уже почти бриллиант.

Помимо всего прочего я работал над проектами и постоянно читал всяческие форумы, блоги, дискутировал с коллегами. Решал задачки разных собеседований.


Отдельного внимания заслуживает книга Банды четырех (Паттерны).
Я ее с трудом дочитал, читал ее я уже аж после всего перечисленного и после примерно 7-8 лет опыта С++.
Я уже давно был Senior dev. и наконец нашел таки время и для нее. Она мне показалась до ужаса скучной и очевидной, поскольку почти все предложенные решения я придумывал и сам неоднократно. Кроме того, большинство этих решений неоправданно тяжелы, и очень запутывают код. Тема холиварная и спать надо много, но я пожалуй остановлюсь только на том что в моей практике, худшими с точки зрения цены ошибок были разработчики которые учились начиная с этой книги. Их код недодерживаем запутан и очень плохо поддается рефакторингу. Такой код имеет самые долго отлеживаемые ошибки.

Где-то рядом я прочитал Фаулера — Рефакторинг. Вполне себе неплохо. Рекомендую. Но тут стоит к опытному коллеге обратиться. Идея то проста Тесты — Маленькие комиты — YAGNI KISS и SRP но детали лучше познавать на практике.
У меня был хороший лид, который меня в конце концов научил 🙂

Совсем забыл! Александреску же! Скажем так — не так страшен Александреску как тот кто его начитался 🙂
Фана доставил много, а так же дал возможность на эти игрища потерять около 3-х месяцев работы. Даже не знаю
стоит ли читать. Наверное стоит, но нужно помнить что на практике лучше не использовать если вы уже не эксперт.

Остальное С++ не касается, но чтобы стать профессионалом Вам потребуются алгоритмы и структуры данных (Кормен, Кнут), многопоточность (Энтони Уильямс), другие языки(питон, JS, java), оптимизация и профилирование.
и много много разных специфических знаний

Удачи Вам в этом нелегком но, безусловно, интереснейшем пути 🙂

0. спасибо. читать книги само собой. главное читать по актуальной версии С++.
1. а почему такой разброс между C++11 C++14 ? разве нельзя взять одну на все случаи жизни так сказать? допустим С++14.

почему-то у меня сложилось впечатление что есть «хардкорные» С++ программисты, которые пишут по старинке под старую, но надежную версиюС ++, и «современные» — которые пишут под С++ 14 + VS 2013 + boost + reSharper. И оба этих лагеря враждуют друг с другом.

Koss1024: я почему спрашиваю так досконально — вот я подтяну знания по С++. приду в компанию — а там все подругому. Другая версия, компилятор другой.

Для меня С++ сейчас в представлении как MASM, TASM, WASM и другие виды ассемблера. И не знаю куда двигаться чтобы не промахнуться.

По поводу книг добавлю «A tour of C++» от Страуструпа. Подкупает своей компактностью — этакий ликбез для С++-программистов. Из минусов то, что там в кучу С++98 и С++11. Ещё «Overview of the New C++ (C++11/14)» от Мейерса. Это презентационные материалы с его тренинга именно по С++11/14, я там был, отличный тренинг. Из минусов — это всё же слайды презентации, какие-то моменты могут быть не понятны без докладчика. Далее, поскольку у Вас уже есть опыт, вполне подойдёт выделить список тем и разботать каждую в отдельности по статьям, записям с конференций, вопросам на SO (или Тостере 🙂 ).

Что именно читать — не так важно, найдёте сами. Чем мне действительно хотелось бы поделиться, так это мыслями по поводу С++11/14 (будем для краткости называть его С++14, т.к. стандарт уже одобрен). Это не просто «новые фишки в С++». Это качественно новый язык. Разница в стилях кодирования между С++14 и С++98 можно сравнить с разницей между С++98 и С. Не такая радикальная, но сравнимая. Возможности С++14 коснулись практически всех аспектов кода. Я позволю себе выделить наиболее значимые, на мой взгляд, изменения, значимые именно для «рядового» программиста, который не пишет буст и не оптимизирует компиляторы:

  • Семантика перемещения*. Контейнеры STL теперь можно возвращать из функции по значению. Строки можно эффективно конкатенировать оператором сложения. Это прекрасно читается.
  • Лямбда-функции. Алгоритмами STL можно, наконец, спокойно пользоваться и не бояться вызвать демонов описанием очередного внешнего шаблонного класса-функтора. Логика работы алгоритма теперь указывается ровно там, где он используется.
  • auto. Казалось бы, это мелочь. Но теперь имя типов итераторов STL не занимает кучу места в программе (ещё очко в пользу STL). Многие конструкции стали более компактными и ушло ненужное дублирование имени типа (принцип DRY!)
  • STL + стандартная библиотека. В С++ чудовищная скудная стандартная библиотека, особенно по сравнению с C#. Но STL, как одна из основных её частей, шагнула вперёд, и не только за счёт других фишек, благодаря которым стало удобно пользоваться тем, что уже было. В ней появились std::unordered_map (хэш-таблицы), std::array и std::tuple — очень полезные в определённых случаях контейнеры. Также добавился ряд полезных алгоритмов (см. доки к заголовочному файлу ). А вы знали, что в С++11 есть класс std::Hash, который позволяет считать хэш от произвольных стандартных типов, включая std::string?
  • std::function. Мало того, что класс предоставляет механизм универсальных колбэков, он позволяет элегантно написать и легко использовать классы типа ScopeGuard (Страуструп называет его finally). Это даёт новый виток развития парадигме RAII, ключевой и уникальной для С++. Ранее для этих целей использовался кривоватый BOOST_SCOPE_EXIT.
  • std::unique_ptr. Благодаря семантике перемещения оказалось возможным создать эффективный умный указатель с монопольным владением объектом. У нас уже был boost::shared_ptr, так что std::shared_ptr не стал таким прорывом.

* Не путать семантику перемещения с r-value references. Последние взрывают мозг. Но это не более чем механизм для реализации семантики перемещения и точной передачи (perfect forwarding). И тем и другим можно прекрасно пользоваться, не заглядывая под капот и не убиваясь в попытках понять, как именно работает эта магия.

По поводу выбора версии Visual Studio. Если есть возможность, лучше использовать VS2013. В ней добавлены такие замечательные и очень полезные возможности С++14, как списки инициализации и инициализация членов класса прямо в месте объявления. В VS2012 этого нет, а по сравнению с VS2010 из интересного в ней появились только range-based циклы, но они не настолько круты. Полный список отличий в MSDN.

В С++14 ещё много, реально МНОГО нового, по сравнению с С++98. Но большая часть либо просто заимствована из boost, так что так или иначе народ этим уже пользовался, либо достаточна специфична. Я постарался выделить именно принципиально новые моменты, которые меняют стиль написания кода. И код на С++14 смотрится гораздо компактнее и понятнее, чем на С++98. Да пребудет с вами С++14. Я кончил. Спасибо.

Как изменить стандарт C ++ в Visual Studio 2013

Моя проблема в том, что в своей книге Бьярне использует некоторые функции из стандарта C ++ 11, который, используя следующий код, который я обнаружил, использовал c ++ 98.

Когда я обнаружил это, я использовал VS 2013 Ultimate на компьютере с Win8.1 с набором инструментов платформы v12.0 или выше. На моем компьютере также установлены VS 2010 и VS 2015 (только что установленный 2015, чтобы увидеть, решена ли проблема).

Ничего из этого не сработало. Помимо набора инструментов платформы, установленного на v12.0, я не нашел никаких других решений онлайн. У моего друга, который запускает VS 2013 и 2015 на своем Mac через параллели, такой проблемы нет.

Этот поток имеет ту же проблему, хотя OP использовал VS 2010:
Как «активировать» стандарт C ++ 11 в Visual Studio 2010?

Я полностью потерян в этот момент и понятия не имею, как это исправить. Без этого стандарта практически невозможно следовать книге Бьярне.

Как мне изменить или обновить мой стандарт C ++.

РЕДАКТИРОВАТЬ / РЕШЕНИЕТаким образом, основная причина, по которой я сделал этот пост, заключалась в том, что, следуя вышеприведенной книге, я столкнулся с проблемой, когда использование списков инициализаторов не было признанной функцией. Согласно другим постам по этому вопросу и предоставленным ссылкам, initializer-lists явно является функцией C ++ 11, поэтому после того, как я использовал приведенный выше код, я предположил, что не использую C ++ 11.

Ниже приведены шаги, которые я предпринял для решения этой проблемы.

  1. Сначала я удалил все распространяемые файлы 2010 года, включая VS 2010.
  2. Через «Программы и компоненты» я запустил мастер удаления VS 2013 и выбрал «восстановить»
  3. Это решило проблему, которую я получал о «списках инициализаторов», которые не были распознаны, однако возникла еще одна ошибка, которую книга не охватывала.

Ошибка и решение, которое я получал в результате устаревшего
std_lib_facilities.h несмотря на то, что я скопировал ссылку прямо из книги .PDF

PS: я не переустанавливал распространяемые файлы 2010 года, так как не вижу, чтобы я нуждался в них в данный момент.

Решение

Проблема для потока, который вы связали, заключается в том, что VS2010 не поддерживает многие функции C ++ 11, такие как std :: thread (что и нужно для этой операции).

VS2013 поддерживает большинство функций C ++ 11. Вы заметите, что если вы делаете проект и #include это должно работать просто отлично для вас.

Чтобы увидеть, какие функции C ++ 11 поддерживает ваш компилятор, проверьте это: Таблица основных характеристик C ++ 11

Другие решения

Использование C ++ 11 в Visual Studio

Вы не можете «активировать или изменить определенный стандарт C ++» в Visual Studio.
Если вы хотите использовать макросы для условной компиляции, вам нужно найти функции, которые вы собираетесь использовать, и версию Visual Studio, поддерживающую его.

Если вы знаете желаемую версию MSVC, вы можете использовать что-то вроде

VS2010 имеет #define _MSC_VER 1600 , VS2012 имеет #define _MSC_VER 1700 и VS2013 имеет #define _MSC_VER 1800 насколько мне известно.

Почему макрос не равен 201103L если C ++ 11 (частично) поддерживается?

Visual Studio не полностью поддерживает C ++ 11: Поддерживаемые функции C ++ 11 в VS2013 .

На самом деле стандарт говорит в записке о __cplusplus :

Предполагается, что будущие версии этого стандарта заменят значение этого макроса на большее значение. Несоответствующие компиляторы должны использовать значение не более пяти десятичных цифр.

Я думаю, что макрос не подходит 201103L до тех пор, пока Visual Studio не полностью соответствует C ++ 11 (если вообще когда-либо;)).

Я верю, что макрос говорит 199711L для обозначения (почти полного) соответствия C ++ 98 (хотя у меня остались функции C ++ 98, которые не включены в VS).

Я добавляю этот ответ на запрос. Этот ответ также можно найти в OP в качестве редактирования.

РЕДАКТИРОВАТЬ / РЕШЕНИЕТаким образом, основная причина, по которой я сделал этот пост, заключалась в том, что, следуя вышеприведенной книге, я столкнулся с проблемой, когда использование списков инициализаторов не было признанной функцией. Согласно другим постам по этому вопросу и предоставленным ссылкам, initializer-lists явно является функцией C ++ 11, поэтому после того, как я использовал приведенный выше код, я предположил, что не использую C ++ 11.

Ниже приведены шаги, которые я предпринял для решения этой проблемы.

Сначала я удалил все распространяемые файлы 2010 года, включая VS 2010. Через «Программы и компоненты» я запустил мастер удаления VS 2013 и выбрал «восстановление». Это решило проблему, которую я получал о «списках инициализаторов», которые не были распознаны, однако возникла еще одна ошибка, которую книга не охватывала.

Ошибка и решение, которое я получал в результате устаревшего std_lib_facilities.h несмотря на то, что я скопировал ссылку прямо из книги .PDF

Ошибку и решение, которое я получил, можно найти здесь: Simple Code Need Help — ни один экземпляр конструктора не соответствует списку аргументов

PS: я не переустанавливал распространяемые файлы 2010 года, так как не вижу, чтобы я нуждался в них в данный момент.

FAQ о написании предложений к стандарту C++

Что это за стандарт?

— Стандарт языка — это толстый документ, исчерпывающе описывающий синтаксис, семантику и даже «правила хорошего тона» для разработчиков. Свои стандарты есть у каждого крупного языка. Когда человек учит язык, фактически он изучает актуальный стандарт. Администрированием стандарта занимается Международная организация по стандартизации — ISO.

Программирование — высокотехнологичная сфера, поэтому языки программирования быстро устаревают и их всё время нужно приспосабливать к новым условиям.

Поэтому в ISO заседают экспертные советы, которые собирают предложения от разработчиков по всему миру и определяют, какие изменения нужно внести стандарты. Совет, отвечающий за C++, утверждает новую версию стандарта раз в 3 года. На очереди версия С»»++»»20 — она появится в 2020 году.

Авторы компиляторов, как правило, реализуют поддержку новой версии стандарта задолго до официального решения ISO. Это позволяет разработчикам поэкспериментировать с нововведениями и сообщить о найденных недочётах.

Что такое предложение к стандарту?

— Экспертный совет небольшой и не может придумать всё сам — большую часть полезных изменений предлагают разработчики, не входящие ни в какие совещательные органы. Можно сказать, что сейчас C++ развивается «коллективным разумом», а не управляется небольшой группой руководителей.

Процедура внесения предложений формализована — разработчик, который придумал что-то полезное, должен представить довольно объёмный документ, написанный по определённым правилам — proposal.

Звучит страшно, но на самом деле облечь хорошую идею в proposal не так уж сложно — особенно с нашей помощью.

Кто может писать предложения?

— Любой опытный разработчик, отлично знающий язык.

На самом деле перспективная идея может прийти в голову и новичку со свежим взглядом, но новичку будет непросто учесть всевозможные тонкости, ограничения и общее направление развития языка.

И что, в самом деле принимают?

— Да. Все отправленные и принятые proposals хранятся на сайте https://www.open-std.org/jtc1/sc22/wg21/docs/papers/. Принимают не всё, конечно, но шанс есть.

А сколько времени занимает написать?

— Ну это как с дипломом или диссертацией. Один пишет за несколько бессонных суток, другой — за несколько лет. Всё зависит от самой идеи и от вас.

Сам proposal по объёму примерно соответствует записке к диплому выпускника технического вуза.

И что, прямо я могу взять и написать?

— Ну да! Пишите уже.

Да бросьте, что я могу придумать? Пусть умные люди пишут.

— Ну вы напишите, а мы посмотрим. Великие начинали с малого. Например, создатель Boost.Filesystem, автор функций для работы с файлами в С++17 начал с того, что попросил вернуть в стандарт случайно выпавшую из него функцию getline.

Что мне это даст? Меня сразу пригласят на работу в Яндекс, Фейсбук и Гугл? Будут звать на лучшие конференции мира?

— Зависит от вашего вклада в C++. Станьте Саттером или Майерсом, и вас будут звать все фирмы мира. Чудес, как обычно, ожидать не стоит, но даже одного proposal может хватить, чтобы прославиться.

Если я приму вашу помощь, мне придётся взять вас в соавторы?

А гонорар заплатят?

Пожалуй, буду пробовать! Как взяться, с чего начать?

C++ и не только

Страницы

пятница, 24 января 2014 г.

История языка C++

История создания

1. является многоцелевым, лаконичным и относительно низкоуровневым языком;
2. подходит для решения большинства системных задач;
3. исполняется везде и на всём;
4. стыкуется со средой программирования UNIX.

Несмотря на ряд известных недостатков языка C, Страуструп пошёл на его использование в качестве основы, так как «в C есть свои проблемы, но их имел бы и разработанный с нуля язык, а проблемы C нам известны». Кроме того, это позволило быстро получить прототип компилятора (cfront), который лишь выполнял трансляцию добавленных синтаксических элементов в оригинальный язык C.

По мере разработки C++ в него были включены другие средства, которые перекрывали возможности конструкций C, в связи с чем неоднократно поднимался вопрос об отказе от совместимости языков путём удаления устаревших конструкций. Тем не менее, совместимость была сохранена из следующих соображений:

  • сохранение действующего кода, написанного изначально на C и прямо перенесённого в C++;
  • исключение необходимости переучивания программистов, ранее изучавших C (им требуется только изучить новые средства C++);
  • исключение путаницы между языками при их совместном использовании («если два языка используются совместно, их различия должны быть или минимальными, или настолько большими, чтобы языки было невозможно перепутать»).
Добавить комментарий