Поделиться через


Поддержка функций C++11 (современный C++)

В этом документе описываются функции C++11, реализованные в Visual C++.

Содержание этой статьи

  • C++11 Feature List

    • Core Language Feature Table

    • Core Language Feature Table: Concurrency

    • Core Language Feature Table: C99

  • Guide To Feature Tables

    • Rvalue References

    • Lambdas

    • decltype

    • Strongly Typed/Forward Declared enums

    • Alignment

    • Standard-Layout and Trivial Types

    • Defaulted and deleted functions

    • override and final

    • Atomics, and more

    • C99 __func__ and preprocessor rules

  • Standard Library features

Список возможностей C+11

В Visual C++ в Visual Studio 2010 реализованы многие возможности из основной спецификации языка C++11, а в Visual C++ в Visual Studio 2012 добавлены дополнительные возможности. В Visual C++ в Visual Studio 2013 эта поддержка дополнительно расширена, а также поддерживаются некоторые возможности библиотеки C++14. В следующей таблице перечислены основные возможности языка C++11 и их состояние реализации в Visual C++ в Visual Studio 2010, Visual C++ в Visual Studio 2012 и Visual C++ в Visual Studio 2013.

Таблица основных возможностей языка C+11

Основные возможности языка C++11

Visual Studio 2010

Visual Studio 2012

Visual Studio 2013

Ссылки Rvalue v0.1, v1.0, v2.0, v2.1, v3.0

v2.0

v2.1*

v2.1*

ref-квалификаторы

Нет

Нет

Нет

Инициализаторы нестатических данных-членов

Нет

Нет

Да

Шаблоны с переменным числом аргументов v0.9, v1.0

Нет

Нет

Да

Списки инициализаторов

Нет

Нет

Да

static_assert

Да

Да

Да

auto v0.9, v1.0

вер. 1.0

вер. 1.0

вер. 1.0

Отслеживание возвращаемых типов

Да

Да

Да

Лямбда-выражения v0.9, v1.0, v1.1

вер. 1.0

v1.1

v1.1

decltype v1.0, v1.1

вер. 1.0

v1.1**

v1.1

Правые угловые скобки

Да

Да

Да

Аргументы шаблонов по умолчанию для шаблонов функций

Нет

Нет

Да

Выражение SFINAE

Нет

Нет

Нет

Шаблоны псевдонимов

Нет

Нет

Да

Шаблоны extern

Да

Да

Да

nullptr

Да

Да

Да

Строго типизированные перечисления

Partial

Да

Да

Перечисления с опережающим объявлением

Нет

Да

Да

Атрибуты

Нет

Нет

Нет

constexpr

Нет

Нет

Нет

Выравнивание

TR1

Partial

Partial

Делегирующие конструкторы

Нет

Нет

Да

Наследование конструкторов

Нет

Нет

Нет

Операторы явного преобразования

Нет

Нет

Да

char16_t/char32_t

Нет

Нет

Нет

Строковые литералы Юникода

Нет

Нет

Нет

Необработанные строковые литералы

Нет

Нет

Да

Универсальные имена символов в литералах

Нет

Нет

Нет

Определенные пользователем литералы

Нет

Нет

Нет

Стандартная раскладка и тривиальные типы

Нет

Да

Да

Установленные по умолчанию и удаленные функции

Нет

Нет

Да*

Расширенные дружественные объявления

Да

Да

Да

Расширенный sizeof

Нет

Нет

Нет

Встроенные пространства имен

Нет

Нет

Нет

Неограниченные объединения

Нет

Нет

Нет

Локальные и неименованные типы как аргументы шаблонов

Да

Да

Да

Цикл for на основе диапазона

Нет

Да

Да

override и final v0.8, v0.9, v1.0

Partial

Да

Да

Минимальная поддержка сборки мусора

Да

Да

Да

noexcept

Нет

Нет

Нет

[go to top]

Таблица основных возможностей языка C+11: параллельность

Основные возможности языка C++11: параллельность

Visual Studio 2010

Visual Studio 2012

Visual Studio 2013

Точки последовательности с измененными формулировками

Н/Д

Н/Д

Н/Д

Атомарные элементы

Нет

Да

Да

Строгое сравнение и обмен

Нет

Да

Да

Двусторонние барьеры

Нет

Да

Да

Модель памяти

Н/Д

Н/Д

Н/Д

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

Нет

Да

Да

Упорядочение зависимостей данных: аннотация функции

Нет

Нет

Нет

exception_ptr

Да

Да

Да

quick_exit

Нет

Нет

Нет

Атомарные элементы в обработчиках сигналов

Нет

Нет

Нет

Локальное хранилище потока

Partial

Partial

Partial

Магическая статика

Нет

Нет

Нет

[go to top]

Основные возможности языка C++11: C99

Основные возможности языка C++11: C99

Visual Studio 2010

Visual Studio 2012

Visual Studio 2013

__func__

Partial

Partial

Partial

Препроцессор C99

Partial

Partial

Partial

long long

Да

Да

Да

Расширенные целочисленные типы

Н/Д

Н/Д

Н/Д

[go to top]

Справочник по таблицам возможностей

Ссылки Rvalue

Примечание

Номера версий (v0.1, v1.0, v2.0, v2.1 и v3.0) в следующих описаниях призваны лишь иллюстрировать эволюцию С++11.В самом стандарте они не используются.

N1610 "Пояснение инициализации объектов класса значениями rvalue" — это была ранняя попытка включить семантику перемещения без ссылок rvalue. Для целей данного обсуждения назовем это "ссылки rvalue v0.1". Эта возможность была заменена на "ссылки rvalue v1.0". Возможность "ссылки rvalue v2.0", на которой была основана работа в Visual C++ в Visual Studio 2010, запрещает привязывать ссылки rvalue к значениям lvalue и тем самым решает серьезную проблему безопасности. " В "ссылках rvalue v2.1" это правило уточняется. Рассмотрим vector<string>::push_back(), где имеются перегрузки push_back(const string&) и push_back(string&&) и вызов v.push_back("strval"). Выражение "strval" — строковый литерал, и это значение lvalue. (Другие литералы, например целое число 1729, представляют собой значения rvalue, но строковые литералы являются особым случаем, потому что они представляют собой массивы.) В правилах по "ссылкам rvalue v2.0" было сказано, что string&& не может привязываться к "strval", так как "strval" — lvalue и, таким образом, push_back(const string&) является единственной допустимой перегрузкой. В результате это привело бы к созданию временного элемента std::string, его копированию в вектор и дальнейшему удалению std::string, что было не очень эффективно. В правилах по "ссылкам rvalue v2.1" признается, что привязка string&& к "strval" приведет к созданию временного элемента std::string, и этот временный элемент представляет собой rvalue. Поэтому можно использовать как push_back(const string&), так и push_back(string&&), причем предпочтительно использовать push_back(string&&). Создается временный элемент std::string, затем он перемещается в вектор. Это более эффективно.

В "ссылках rvalue v3.0" добавляются новые правила для автоматического создания конструкторов перемещения и операторов перемещения с присваиванием в определенных условиях. Однако это не реализовано в Visual C++ в Visual Studio 2013 из-за ограниченного времени и ресурсов.

[go to top]

Lambdas

После того как в результате голосования лямбда-функции были включены в рабочий план (версия "0.9") и были добавлены изменяемые лямбда-выражения (версия "1.0"), комитет по стандартизации полностью переработал формулировки. В результате создаются лямбда-выражения версии "1.1", которая теперь полностью поддерживается. Формулировка лямбда-выражений v1.1 уточняет, что должно произойти в патологических случаях, таких как обращение к статическим элементам или вложенным лямбда-выражениям. Это устраняет проблемы, которые активируются сложными лямбда-выражениями. Кроме того, не имеющие сведений о состоянии лямбда-выражения теперь можно преобразовывать в указатели функций. Это не содержится в формулировках N2927, но все равно считается частью лямбда-выражений v1.1. C++11 5.1.2 [expr.prim.lambda]/6 имеет такое описание: "Тип закрытия lambda-expression без lambda-capture имеет открытую невиртуальную неявную константную функцию преобразования в указатель на функцию, имеющую те же типы параметров и возвращаемых значений, что и оператор вызова функции типа закрытия. Значение, возвращаемое этой функцией преобразования, будет адресом функции, которая при вызове приводит к тому же результату, что и вызов оператора вызова функции типа закрытия." (Реализация Visual C++ в Visual Studio 2012 в этом плане даже лучше, потому что позволяет преобразовывать не имеющие сведений о состоянии лямбда-выражения в указатели функций, имеющие произвольные соглашения о вызовах. Это важно при использовании API, ожидающих такие действия, как указатели функций __stdcall).

[go to top]

decltype

После того как в результате голосования decltype включили в рабочий план (версия 1.0), здесь в последнюю минуту была исправлена небольшая, но важная ошибка (версия 1.1). Это представляет большой интерес для программистов, работающих на STL и Boost.

[go to top]

Строго типизированные перечисления и перечисления с опережающим объявлением

Строго типизированные перечисления частично поддерживаются в Visual C++ в Visual Studio 2010 (в частности, в том, что касается явно определенных базовых типов). Теперь они полностью реализованы в Visual Studio, и семантика C++11 для перечислений с опережающим объявлением также полностью реализована.

[go to top]

Выравнивание

Основные ключевые слова языка alignas/alignof из предложения выравнивания, которое было включено в рабочий план по итогам голосования, не реализованы. В Visual C++ в Visual Studio 2010 было aligned_storage из TR1. В Visual C++ в Visual Studio 2012 в стандартную библиотеку добавлены aligned_union и std::align(), а также были устранены значительные проблемы в Visual C++ в Visual Studio 2013.

[go to top]

Стандартная раскладка и тривиальные типы

К очевидным изменениям, указанным в статье N2342 "POD's Revisited; Resolving Core Issue 568 (Revision 5)", относятся добавление в стандартную библиотеку шаблонов <type_traits> характеристик is_trivial и is_standard_layout. (В N2342 переработаны многие формулировки базового языка, однако изменения в компиляторе не требуются.) Эти характеристики типов доступны в Visual C++ в Visual Studio 2010, но они просто дублируют is_pod. Поэтому в таблице ранее в этом документе было указание об отсутствии поддержки. Они теперь существуют на базе обработчиков компилятора, предназначенных для предоставления точных ответов.

Тип common_type<> из STL получил столь необходимое исправление в Visual C++ в Visual Studio 2013. Спецификация C++11 для common_type<> имела неожиданные и нежелательные последствия; в частности, в соответствии с ней common_type<int, int>::type возвращает int&&. Поэтому в Visual C++ в Visual Studio 2013 реализуется предлагаемое решение проблемы рабочей группы библиотек 2141, которое заставляет common_type<int, int>::type возвращать int.

В качестве побочного эффекта этого изменения вариант с идентификатором больше не работает (common_type<T> не всегда дает тип T). Это соответствует предлагаемому решению, однако нарушает работу кода, в котором предполагается ранее существовавшее поведение.

Если требуется характеристика типа идентификатора, не используйте нестандартную структуру std::identity, определенную в <type_traits>, поскольку она не будет работать для <void>. Вместо этого реализуйте собственную характеристику типа идентификатора в соответствии со своими потребностями. Ниже приведен пример:

template <typename T> struct Identity {
    typedef T type;
};

Примечание

Другие критические изменения см. в разделе Критические изменения в Visual C++.

[go to top]

Установленные по умолчанию и удаленные функции

Они теперь поддерживаются, но с одним исключением. Для функций, заданных по умолчанию, не поддерживается использование =default для запроса конструкторов перемещения с учетом членов и операторов перемещений с присваиванием. Копирование и перемещение не взаимодействуют в точном соответствии с устанавливающим это стандартом, например удаление перемещений, согласно стандарту, также должно подавлять копии, однако Visual C++ в Visual Studio 2013 не делает этого.

Дополнительные сведения об использовании установленных по умолчанию и удаленных функций см. в разделе Определения функций в C++.

[go to top]

override и final

Здесь пройдено короткое, но сложное развитие. Первоначально в версии 0.8 были атрибуты [[override]], [[hiding]] и [[base_check]]. Затем в версии 0.9 эти атрибуты были исключены и заменены контекстными ключевыми словами. Наконец, в версии 1.0 они были уменьшены до "final" для классов и "override" и "final" для функций. Это делает его возведенным расширением, поскольку Visual C++ в Visual Studio 2010 уже поддерживал этот синтаксис "override" для функций, а семантика была достаточно близка семантике С++11. "final" также поддерживалось, но в другом написании — "sealed". Стандартные записи и семантика "override" и "final" теперь полностью поддерживаются. Дополнительные сведения см. в разделах Спецификатор override и Спецификатор final.

[go to top]

Атомарные элементы и другое

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

Связанные заголовки STL: <atomic>, <chrono>, <condition_variable>, <future>, <mutex>, <ratio>, <scoped_allocator> и <thread>.

[go to top]

__func__ и правила препроцессора C99

В таблице Core Language Feature Table: C99 описывается частичная реализация этих двух компонентов. Для предварительно определенного идентификатора __func__ реализация является частичной по той причине, что поддерживаются нестандартные расширения __FUNCDNAME__, __FUNCSIG__ и __FUNCTION__. Для получения дополнительной информации см. Предустановленный макрос. Для правил препроцессора C99 реализация является частичной по той причине, что поддерживаются макросы с переменным числом аргументов. Для получения дополнительной информации см. Макрос со списками аргументов переменных.

[go to top]

Стандартные возможности библиотеки

Это охватывает основной язык. Что касается стандартной библиотеки C++11, для нее нет красивой таблицы сравнения функций, однако Visual C++ в Visual Studio 2012 реализует ее за двумя исключениями. Во-первых, когда функция библиотеки зависела от функциональности, отсутствующей в компиляторе, она либо имитировалась — например, имитируемые шаблоны переменного количества аргументов для make_shared<T>() — либо не реализовывалась. (Было лишь несколько вариантов, самый заметный — <initializer_list>, которые теперь полностью реализованы в Visual C++ в Visual Studio 2013). За очень немногими исключениями, C99 реализован в предоставляемых заголовках-оболочках C++ и Visual C++ в Visual Studio 2013. Подробнее см. в статье Библиотечная поддержка C99 в Visual Studio 2013.

Здесь приводится частичный список изменений в Visual C++ в Visual Studio 2012 и Visual C++ в Visual Studio 2013:

Назначение места. Согласно требованиям C++11 emplace()/emplace_front()/emplace_back()/emplace_hint()/emplace_after() реализованы во всех контейнерах для "произвольного" количества аргументов (см. подраздел "Имитируемая поддержка переменного числа аргументов"). Например, в vector<T> есть метод "template <typename... Args> void emplace_back(Args&&... args)", который непосредственно создает элемент типа T с обратной стороны вектора из произвольного числа произвольных аргументов с прекрасным опережением. Это может быть более эффективным, чем push_back(T&&), что предполагало бы дополнительное создание и уничтожение перемещения.

Переменное число аргументов. В Visual C++ в Visual Studio 2012 была предусмотрена схема для имитации шаблонов с переменным количеством аргументов. В Visual C++ в Visual Studio 2013 имитация больше не используется, т. к. поддержка переменного числа аргументов полностью реализована. Если в коде используется старое поведение имитируемой поддержки переменного числа аргументов, необходимо исправить его. Тем не менее переход к реальным шаблонам с переменным числом аргументов позволил улучшить время компиляции и сократить использование памяти компилятором.

Операторы явного преобразования. В базовом языке операторы явного преобразования являются общей возможностью, например, возможно использование оператора explicit operator MyClass(). Однако стандартная библиотека в настоящее время использует только одну форму: explicit operator bool(), которая делает классы полностью логически проверяемыми. (Простое "operator bool()" заведомо небезопасно.) Ранее в Visual C++ имитировалось explicit operator bool() с operator pointer-to-member(), что привело к различным проблемам и неэффективной работе. Теперь этот обходной путь с "имитируемой логикой" полностью отвергнут.

Случайность. Теперь uniform_int_distribution является полностью несмещенным, а shuffle() реализован в <algorithm>, который непосредственно принимает генераторы равномерных случайных чисел, такие как mersenne_twister.

Устойчивость к перегруженным операторам address-of. C++98/03 запрещал элементу из контейнеров STL перегружать его оператор address-of. Это то, что обеспечивают классы CComPtr, чтобы вспомогательные классы, такие как CAdapt, требовались для защиты STL от таких перегрузок. Во время разработки Visual C++ в Visual Studio 2010 из-за изменений, внесенных в STL, перегруженные операторы address-of стали отклоняться еще чаще. В C++11 требования были изменены, чтобы сделать перегруженные операторы address-of допустимыми. C++11 и Visual C++ в Visual Studio 2010 предоставляют вспомогательную функцию std::addressof(), которая может получать истинный адрес объекта вне зависимости от перегрузки операторов. До выпуска Visual C++ в Visual Studio 2010 мы пытались заменять вхождения "&elem" функцией "std::addressof(elem)", которая достаточно устойчива. Разработчики Visual C++ в Visual Studio 2012 пошли дальше, чтобы классы, которые перегружают свой оператор address-of, могли использоваться во всей STL.

Visual C++ в Visual Studio 2012 вышел за рамки C++11 несколькими способами:

Итераторы SCARY. Как разрешено, хотя и не требуется в С++11 Standard, итераторы SCARY были реализованы, как описано в статьях N2911. "Сворачивание зависимостей в универсальных классах для более быстрых и небольших программ" и N2980 "Назначение и инициализация итераторов SCARY, редакция 1".

Filesystem. Добавлен заголовок <filesystem> из предложения TR2. Он обеспечивает recursive_directory_iterator и другие интересные возможности. Прежде чем работа над TR2 была заморожена, поскольку C++0x разрабатывался с большим опозданием и менялся на C++11, это выдвинутое в 2006 г. предложение было основано на Boost.Filesystem V2. Позже он был преобразован в Boost.Filesystem V3, но не реализуется в Visual C++ в Visual Studio 2012.

И существенная оптимизация! Все наши контейнеры теперь оптимально уменьшены с учетом их текущих представлений. Это относится к самим объектам-контейнерам, а не к их содержимому, на которое существуют указатели. Например, std::vector содержит 3 необработанных указателя. В Visual C++ в Visual Studio 2010, в режиме выпуска для x86, std::vector имел размер 16 байт. В Visual C++ в Visual Studio 2012 размер составляет 12 байта, что является оптимальным минимумом. Это важно: при наличии 100 000 векторов в программе Visual C++ в Visual Studio 2012 экономит 400 000 байт. Сокращение использования памяти экономит и место, и время.

Это было достигнуто путем отказа от хранения пустых распределителей и средств сравнения, поскольку std::allocator и std::less не имеют состояния. (Эти оптимизации разрешены и для пользовательских распределителей и средств сравнения при условии, что они не имеют сведений о состоянии. Очевидно, что от хранения распределителей и средств сравнения с отслеживанием состояния не удастся отказаться, но они встречаются очень редко).

Возможности Visual C++ в Visual Studio 2013 выходят еще дальше за рамки возможностей C++11; так, реализованы некоторые ключевые возможности библиотеки C++14:

  • "Прозрачные функторы операторов" less<>, greater<>, plus<>, multiplies<> и т. д.

  • make_unique<T>(args...) и make_unique<T[]>(n)

  • Функции, не являющиеся членами — cbegin()/cend(), rbegin()/rend() и crbegin()/crend().

[go to top]

См. также

Ссылки

Лямбда-выражения в C++

Основанное на диапазоне выражение for (C++)

Основные понятия

Новые возможности Visual C++ в Visual Studio 2013

Критические изменения в Visual C++

Другие ресурсы

Возвращение к C++ (современный C++)

Справочник по языку C++

Справочник по стандартной библиотеке C++

Блог команды разработчиков Visual C++