WikiDer > C ++ 11

C++11

C ++ 11 это версия стандарта для языка программирования C ++. Это было одобрено Международная организация по стандартизации (ISO) 12 августа 2011 г., заменив С ++ 03,[1] заменен C ++ 14 18 августа 2014 г.[2] а позже C ++ 17. Название следует традиции именования языковых версий по году публикации спецификации, хотя ранее оно называлось C ++ 0x потому что ожидалось, что он будет опубликован до 2010 года.[3]

Хотя одна из целей дизайна заключалась в том, чтобы предпочесть изменения в библиотеках изменениям в основной язык,[4] В C ++ 11 есть несколько дополнений к основному языку. Значительно улучшенные области основного языка включают поддержку многопоточности, общее программирование поддержка, единообразная инициализация и производительность. Значительные изменения были внесены и в Стандартная библиотека C ++, включающий большую часть Технический отчет C ++ 1 (TR1) библиотеки, кроме библиотеки математических специальных функций.[5]

C ++ 11 был опубликован как ИСО / МЭК 14882: 2011[6] в сентябре 2011 года и предоставляется за отдельную плату. Рабочий проект, наиболее похожий на опубликованный стандарт C ++ 11, - это N3337 от 16 января 2012 г .;[7] он содержит только редакционные исправления стандарта C ++ 11.[8]

Цели дизайна

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

  • Поддерживать стабильность и совместимость с C ++ 98 и, возможно, с C
  • Предпочитайте вводить новые функции через стандартную библиотеку, а не расширять базовый язык
  • Предпочитайте изменения, которые могут развить технику программирования
  • Улучшайте C ++ для облегчения проектирования систем и библиотек, а не вводите новые функции, полезные только для определенных приложений.
  • Повышение безопасности типов за счет предоставления более безопасных альтернатив ранее использовавшимся небезопасным методам
  • Повышение производительности и возможность работы напрямую с оборудованием
  • Предоставлять правильные решения для реальных проблем
  • Воплощать в жизнь нулевые накладные расходы принцип (дальнейшая поддержка, необходимая для некоторых утилит, должна использоваться только в том случае, если эта утилита используется)
  • Упростите обучение и изучение C ++, не удаляя никаких утилит, необходимых опытным программистам

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

Расширения основного языка C ++

Одна из функций комитета C ++ - это разработка ядра языка. Были значительно улучшены следующие области основного языка: многопоточность поддерживать, общее программирование поддержка, единообразная инициализация и производительность.

Улучшения производительности среды выполнения основного языка

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

Ссылки Rvalue и конструкторы перемещения

В C ++ 03 (и ранее) временные (называемые "rvalues", так как они часто находятся справа от задания) никогда не подлежали изменению - так же, как в C - и считались неотличимыми от const T & типы; тем не менее, в некоторых случаях временные конструкции могли быть изменены, что даже считалось полезной лазейкой.[9] C ++ 11 добавляет новый неконстантный ссылочный тип, называемый ссылка rvalue, идентифицированный T &&. Это относится к временным файлам, которые разрешено изменять после их инициализации с целью обеспечения «семантики перемещения».

Хроническая проблема с производительностью C ++ 03 - дорогостоящая и ненужная глубокие копии это может происходить неявно, когда объекты передаются по значению. Чтобы проиллюстрировать проблему, представьте, что std :: vector внутренне является оболочкой для массива в стиле C с определенным размером. Если std :: vector временный создается или возвращается из функции, его можно сохранить только путем создания нового std :: vector и копируя в него все данные rvalue. Тогда временное и вся его память уничтожаются. (Для простоты в данном обсуждении не учитывается оптимизация возвращаемого значения.)

В C ++ 11 переместить конструктор из std :: vector который принимает ссылку rvalue на std :: vector может скопировать указатель на внутренний массив в стиле C из rvalue в новое std :: vector , затем установите указатель внутри rvalue на null. Поскольку временный объект больше никогда не будет использоваться, никакой код не будет пытаться получить доступ к нулевому указателю, а поскольку указатель является нулевым, его память не удаляется, когда он выходит за пределы области видимости. Следовательно, операция не только не требует затрат на глубокое копирование, но и безопасна и невидима.

Ссылки Rvalue могут повысить производительность существующего кода без необходимости вносить какие-либо изменения за пределами стандартной библиотеки. Тип возвращаемого значения функции, возвращающей std :: vector временный не требует явного изменения на std :: vector && для вызова конструктора перемещения, поскольку временные значения автоматически считаются значениями r. (Однако если std :: vector является версией C ++ 03 без конструктора перемещения, то конструктор копирования будет вызываться с const std :: vector &, что приводит к значительному выделению памяти.)

Из соображений безопасности накладываются некоторые ограничения. Именованная переменная никогда не будет считаться rvalue, даже если она объявлена ​​как таковая. Чтобы получить rvalue, шаблон функции std :: move () должен быть использован. Ссылки Rvalue также могут быть изменены только при определенных обстоятельствах, они предназначены для использования в основном с конструкторами перемещения.

Из-за характера формулировки ссылок rvalue и некоторых изменений формулировки ссылок lvalue (регулярные ссылки) ссылки rvalue позволяют разработчикам обеспечить идеальную пересылку функций. В сочетании с вариативные шаблоны, эта возможность позволяет использовать шаблоны функций, которые могут идеально передавать аргументы другой функции, которая принимает эти конкретные аргументы. Это наиболее полезно для пересылки параметров конструктора, чтобы создать фабричные функции, которые будут автоматически вызывать правильный конструктор для этих конкретных аргументов. Это видно в emplace_back набор методов стандартной библиотеки C ++.

constexpr - Обобщенные константные выражения

В C ++ всегда была концепция постоянных выражений. Это такие выражения, как 3+4 это всегда будет давать одни и те же результаты во время компиляции и во время выполнения. Постоянные выражения - это возможности оптимизации для компиляторов, и компиляторы часто выполнить их во время компиляции и жестко закодируйте результаты в программе. Кроме того, в некоторых местах спецификация C ++ требует использования постоянных выражений. Для определения массива требуется постоянное выражение, а значения перечислителя должны быть постоянными выражениями.

Однако постоянному выражению никогда не разрешалось содержать вызов функции или конструктор объекта. Поэтому такой простой фрагмент кода недействителен:

int get_five() {возвращаться 5;}int some_value[get_five() + 7]; // Создаем массив из 12 целых чисел. Плохо сформированный C ++

Это было недопустимо в C ++ 03, потому что get_five () + 7 не является постоянным выражением. Компилятор C ++ 03 не может узнать, get_five () фактически является постоянным во время выполнения. Теоретически эта функция может влиять на глобальную переменную, вызывать другие функции, не связанные с константой времени выполнения, и т. Д.

В C ++ 11 появилось ключевое слово constexpr, что позволяет пользователю гарантировать, что функция или конструктор объекта является константой времени компиляции.[10] Приведенный выше пример можно переписать следующим образом:

constexpr int get_five() {возвращаться 5;}int some_value[get_five() + 7]; // Создаем массив из 12 целых чисел. Действительный C ++ 11

Это позволяет компилятору понять и проверить, что get_five () - константа времени компиляции.

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

До C ++ 11 значения переменных можно было использовать в постоянных выражениях, только если переменные объявлены как константы, имеют инициализатор, который является константным выражением, и имеют целочисленный или перечисляемый тип. C ++ 11 снимает ограничение, что переменные должны быть целочисленного или перечислимого типа, если они определены с помощью constexpr ключевое слово:

constexpr двойной earth_gravitational_acceleration = 9.8;constexpr двойной moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0;

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

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

Конструктор копирования для типа с любым constexpr конструкторы обычно также следует определять как constexpr конструктор, позволяющий возвращать объекты данного типа по значению из функции constexpr. Любая функция-член класса, такая как конструкторы копирования, перегрузки операторов и т. Д., Может быть объявлена ​​как constexpr, если они соответствуют требованиям для функций constexpr. Это позволяет компилятору копировать объекты во время компиляции, выполнять над ними операции и т. Д.

Если функция или конструктор constexpr вызывается с аргументами, которые не являются постоянными выражениями, вызов ведет себя так, как если бы функция не была constexpr, и полученное значение не является постоянным выражением. Аналогичным образом, если выражение в операторе return функции constexpr не оценивается как постоянное выражение для данного вызова, результат не является постоянным выражением.

constexpr отличается от Consteval, введенный в C ++ 20, в том, что последний всегда должен создавать постоянную времени компиляции, а constexpr не имеет этого ограничения.

Модификация определения простых старых данных

В C ++ 03 класс или структура должны следовать ряду правил, чтобы считаться простые старые данные (POD) типа. Типы, соответствующие этому определению, создают макеты объектов, совместимые с C, и их также можно инициализировать статически. Стандарт C ++ 03 имеет ограничения на то, какие типы совместимы с C или могут быть статически инициализированы, несмотря на отсутствие технических причин, по которым компилятор не мог принять программу; если кто-то создаст тип POD C ++ 03 и добавит невиртуальную функцию-член, этот тип больше не будет типом POD, его нельзя будет статически инициализировать и он будет несовместим с C, несмотря на отсутствие изменений в макете памяти. .

C ++ 11 ослабил некоторые правила POD, разделив концепцию POD на две отдельные концепции: банальный и стандартная планировка.

Тип, который банальный может быть статически инициализирован. Это также означает, что можно копировать данные через memcpyвместо того, чтобы использовать конструктор копирования. Время жизни банальный type начинается, когда его хранилище определено, а не после завершения конструктора.

Тривиальный класс или структура определяется как тот, который:

  1. Имеет тривиальный конструктор по умолчанию. Это может использовать синтаксис конструктора по умолчанию (SomeConstructor () = по умолчанию;).
  2. Имеет тривиальные конструкторы копирования и перемещения, которые могут использовать синтаксис по умолчанию.
  3. Имеет тривиальные операторы присваивания копирования и перемещения, которые могут использовать синтаксис по умолчанию.
  4. Имеет тривиальный деструктор, который не должен быть виртуальным.

Конструкторы тривиальны только в том случае, если нет виртуальных функций-членов класса и нет виртуальных базовых классов. Операции копирования / перемещения также требуют, чтобы все нестатические элементы данных были тривиальными.

Тип, который стандартная планировка означает, что он упорядочивает и упаковывает свои члены способом, совместимым с C. Класс или структура являются стандартными по определению при условии:

  1. В нем нет виртуальных функций
  2. У него нет виртуальных базовых классов
  3. Все его нестатические элементы данных имеют одинаковый контроль доступа (общедоступный, частный, защищенный).
  4. Все его нестатические элементы данных, включая любые его базовые классы, находятся в одном и том же классе в иерархии.
  5. Вышеупомянутые правила также применяются ко всем базовым классам и ко всем нестатическим элементам данных в иерархии классов.
  6. Он не имеет базовых классов того же типа, что и первый определенный нестатический член данных.

Класс / структура / объединение считается POD, если он является тривиальным, стандартным, и все его нестатические элементы данных и базовые классы являются POD.

Разделив эти концепции, можно отказаться от одного, не потеряв другого. Класс со сложными конструкторами перемещения и копирования может быть нетривиальным, но он может иметь стандартный макет и, таким образом, взаимодействовать с C. Точно так же класс с общедоступными и частными нестатическими элементами данных не будет стандартным макетом, но он может быть тривиально и поэтому memcpy-способный.

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

Внешний шаблон

В C ++ 03 компилятор должен создавать экземпляр шаблона всякий раз, когда полностью указанный шаблон встречается в единице перевода. Если шаблон создается с одними и теми же типами во многих единицах перевода, это может значительно увеличить время компиляции. В C ++ 03 нет способа предотвратить это, поэтому в C ++ 11 введены объявления шаблонов extern, аналогичные объявлениям extern данных.

В C ++ 03 есть следующий синтаксис, который заставляет компилятор создавать экземпляр шаблона:

шаблон учебный класс стандартное::вектор<Мой класс>;

C ++ 11 теперь предоставляет такой синтаксис:

внешний шаблон учебный класс стандартное::вектор<Мой класс>;

который сообщает компилятору нет для создания экземпляра шаблона в этой единице перевода.

Улучшения удобства использования основного языка

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

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

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

структура Объект{    плавать первый;    int второй;};Объект скаляр = {0,43f, 10}; // Один объект с первым = 0,43f и вторым = 10Объект массив[] = {{13,4f, 3}, {43,28f, 29}, {5,934f, 17}}; // Массив из трех объектов

Это очень полезно для статических списков или инициализации структуры некоторым значением. C ++ также предоставляет конструкторы для инициализации объекта, но они часто не так удобны, как список инициализаторов. Однако C ++ 03 разрешает списки инициализаторов только для структур и классов, которые соответствуют определению Plain Old Data (POD); C ++ 11 расширяет списки инициализаторов, поэтому их можно использовать для всех классов, включая стандартные контейнеры, такие как std :: vector.

C ++ 11 связывает концепцию с шаблоном, который называется std :: initializer_list. Это позволяет конструкторам и другим функциям принимать списки инициализаторов в качестве параметров. Например:

учебный класс SequenceClass{общественный:    SequenceClass(стандартное::initializer_list<int> список);};

Это позволяет SequenceClass быть построенным из последовательности целых чисел, например:

SequenceClass some_var = {1, 4, 5, 6};

Этот конструктор представляет собой особый тип конструктора, называемый конструктором списка инициализаторов. Классы с таким конструктором обрабатываются специально при унифицированной инициализации (см. ниже)

Класс шаблона std :: initializer_list <> это первый класс Тип стандартной библиотеки C ++ 11. Они могут быть построены статически компилятором C ++ 11 с помощью {} синтаксис без имени типа в контекстах, где такие фигурные скобки приводят к std :: initializer_list, или явно указав тип, например std :: initializer_list {args} (и так далее для других разновидностей синтаксиса конструкции).

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

Хотя его конструкция специально обрабатывается компилятором, std :: initializer_list является реальным типом, поэтому его можно использовать не только в конструкторах классов, но и в других местах. Обычные функции могут принимать типизированные std :: initializer_lists в качестве аргументов. Например:

пустота имя_функции(стандартное::initializer_list<плавать> список); // Копирование дешево; см. вышеимя_функции({1.0f, -3,45f, -0,4f});

Примеры этого в стандартной библиотеке включают std :: min () и std :: max () шаблоны принимая std :: initializer_lists числового типа.

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

стандартное::вектор<стандартное::нить> v = { "xyzzy", "плуг", "абракадабра" };стандартное::вектор<стандартное::нить> v({ "xyzzy", "плуг", "абракадабра" });стандартное::вектор<стандартное::нить> v{ "xyzzy", "плуг", "абракадабра" }; // см. «Единая инициализация» ниже

Единая инициализация

C ++ 03 имеет ряд проблем с инициализацией типов. Существует несколько способов сделать это, и некоторые из них при замене дают разные результаты. Например, традиционный синтаксис конструктора может выглядеть как объявление функции, и необходимо предпринять шаги, чтобы гарантировать, что самый досадный разбор Правило не примет его за такое. Только агрегаты и типы POD могут быть инициализированы агрегатными инициализаторами (используя SomeType var = {/ * материал * /};).

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

структура BasicStruct{    int Икс;    двойной у;};структура AltStruct{    AltStruct(int Икс, двойной у)        : Икс_{Икс}        , y_{у}    {}частный:    int Икс_;    двойной y_;};BasicStruct var1{5, 3.2};AltStruct var2{2, 4.3};

Инициализация var1 ведет себя точно так же, как если бы это была агрегированная инициализация. То есть каждый член данных объекта, в свою очередь, будет инициализирован копией с соответствующим значением из списка инициализаторов. При необходимости будет использоваться неявное преобразование типа. Если преобразование отсутствует или существует только сужающее преобразование, программа сформирована неправильно. Инициализация var2 вызывает конструктор.

Также можно сделать это:

структура IdString{    стандартное::нить имя;    int идентификатор;};IdString get_string(){    возвращаться {"фу", 42}; // Обратите внимание на отсутствие явного типа.}

Единая инициализация не заменяет синтаксис конструктора, который иногда все еще требуется. Если у класса есть конструктор списка инициализаторов (TypeName (список_инициализаторов );), то он имеет приоритет над другими формами построения, при условии, что список инициализаторов соответствует типу конструктора последовательности. Версия C ++ 11 std :: vector имеет конструктор списка инициализаторов для своего типа шаблона. Таким образом, этот код:

стандартное::вектор<int> the_vec{4};

вызовет конструктор списка инициализаторов, а не конструктор std :: vector который принимает один параметр размера и создает вектор с этим размером. Чтобы получить доступ к последнему конструктору, пользователю необходимо напрямую использовать стандартный синтаксис конструктора.

Вывод типа

В C ++ 03 (и C) для использования переменной необходимо явно указать ее тип. Однако с появлением типов шаблонов и методов метапрограммирования шаблонов тип чего-либо, особенно четко определенного возвращаемого значения функции, может быть нелегко выразить. Таким образом, хранение промежуточных продуктов в переменных затруднено, возможно, потребуется знание внутреннего устройства данной библиотеки метапрограммирования.

C ++ 11 позволяет смягчить это двумя способами. Во-первых, определение переменной с явной инициализацией может использовать авто ключевое слово.[11][12] Это создает переменную определенного типа инициализатора:

авто some_strange_callable_type = стандартное::связывать(&some_function, _2, _1, some_object);авто other_variable = 5;

Тип some_strange_callable_type просто независимо от того, какое конкретное переопределение функции шаблона std :: bind возвращается для этих конкретных аргументов. Этот тип легко определяется компилятором процедурно в рамках его обязанностей по семантическому анализу, но пользователю нелегко определить его при проверке. other_variable также хорошо определен, но его легче определить пользователю. Это int, который имеет тот же тип, что и целочисленный литерал.

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

Далее ключевое слово decltype может использоваться для определения типа выражения во время компиляции. Например:

int some_int;decltype(some_int) other_integer_variable = 5;

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

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

за (стандартное::вектор<int>::const_iterator итр = Myvec.cbegin(); итр != Myvec.уступать(); ++итр)

программист может использовать более короткий

за (авто итр = Myvec.cbegin(); итр != Myvec.уступать(); ++итр)

который может быть дополнительно уплотнен, так как "myvec" реализует итераторы начала / конца:

за (авто& Икс : Myvec)

Эта разница увеличивается по мере того, как программист начинает вкладывать контейнеры, хотя в таких случаях typedefs - хороший способ уменьшить объем кода.

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

#включают <vector>int главный(){    const стандартное::вектор<int> v(1);    авто а = v[0];        // a имеет тип int    decltype(v[0]) б = 1; // b имеет тип const int &, тип возврата                          // std :: vector  :: operator [] (size_type) const    авто c = 0;           // c имеет тип int    авто d = c;           // d имеет тип int    decltype(c) е;        // e имеет тип int, тип сущности, названной c    decltype((c)) ж = c;  // f имеет тип int &, потому что (c) является lvalue    decltype(0) грамм;        // g имеет тип int, потому что 0 - это rvalue}

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

C ++ 11 расширяет синтаксис за оператор, позволяющий легко выполнять итерацию по диапазону элементов:

int my_array[5] = {1, 2, 3, 4, 5};// удваиваем значение каждого элемента в my_array:за (int& Икс : my_array)    Икс *= 2;// аналогично, но также с использованием вывода типа для элементов массиваза (авто& Икс : my_array)    Икс *= 2;

Эта форма за, называемый «на основе диапазона для», будет перебирать каждый элемент в списке. Он будет работать для массивов в стиле C, списков инициализаторов и любого типа, который имеет начинать() и конец() определенные для него функции, возвращающие итераторы. Все стандартные библиотечные контейнеры с парами начало / конец будут работать с оператором for на основе диапазона.

Лямбда-функции и выражения

C ++ 11 предоставляет возможность создавать анонимные функции, называемые лямбда-функциями.[13]Они определены следующим образом:

[](int Икс, int у) -> int { возвращаться Икс + у; }

Тип возврата (-> int в этом примере) можно опустить, если все возвращаться выражения возвращают тот же тип. лямбда может быть необязательно закрытие.

Альтернативный синтаксис функции

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

шаблон<учебный класс Lhs, учебный класс Rhs>  Ret add_func(const Lhs &lhs, const Rhs &rhs) {возвращаться lhs + rhs;} // Ret должен быть типа lhs + rhs

Тип Ret независимо от добавления типов Lhs и Rhs будет производить. Даже с вышеупомянутой функциональностью C ++ 11 decltype, это невозможно:

шаблон<учебный класс Lhs, учебный класс Rhs>  decltype(lhs+rhs) add_func(const Lhs &lhs, const Rhs &rhs) {возвращаться lhs + rhs;} // Недействительный C ++ 11

Это недопустимый C ++, потому что lhs и rhs еще не определены; они не будут действительными идентификаторами до тех пор, пока синтаксический анализатор не проанализирует остальную часть прототипа функции.

Чтобы обойти это, C ++ 11 представил новый синтаксис объявления функции с конечный-возвращаемый-тип:

шаблон<учебный класс Lhs, учебный класс Rhs>  авто add_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {возвращаться lhs + rhs;}

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

структура SomeStruct{    авто func_name(int Икс, int у) -> int;};авто SomeStruct::func_name(int Икс, int у) -> int{    возвращаться Икс + у;}

Использование ключевого слова «auto» в этом случае является только частью синтаксиса и не выполняет автоматический выбор типа.[14]

Улучшение строительства объекта

В C ++ 03 конструкторам класса не разрешается вызывать другие конструкторы в списке инициализаторов этого класса. Каждый конструктор должен сам создать все члены своего класса или вызвать общую функцию-член, как показано ниже:

учебный класс SomeType{общественный:    SomeType(int новый номер)    {        Построить(новый номер);    }    SomeType()    {        Построить(42);    }частный:    пустота Построить(int новый номер)    {        номер = новый номер;    }    int номер;};

Конструкторы базовых классов не могут быть напрямую представлены производным классам; каждый производный класс должен реализовывать конструкторы, даже если конструктор базового класса будет подходящим. Непостоянные члены данных классов не могут быть инициализированы на месте объявления этих членов. Их можно инициализировать только в конструкторе.

C ++ 11 предоставляет решения всех этих проблем.

C ++ 11 позволяет конструкторам вызывать другие одноранговые конструкторы (называемые делегация). Это позволяет конструкторам использовать поведение другого конструктора с минимумом добавленного кода. Делегирование использовалось на других языках, например, Ява и Цель-C.

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

учебный класс SomeType{    int номер;общественный:    SomeType(int новый номер) : номер(новый номер) {}    SomeType() : SomeType(42) {}};

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

Это связано с предостережением: C ++ 03 считает, что объект будет создан, когда его конструктор завершит выполнение, но C ++ 11 считает, что объект был создан один раз. любой конструктор завершает выполнение. Поскольку будет разрешено выполнение нескольких конструкторов, это будет означать, что каждый делегирующий конструктор будет выполняться на полностью построенном объекте своего собственного типа. Конструкторы производных классов будут выполняться после завершения всего делегирования в их базовых классах.

Для конструкторов базового класса C ++ 11 позволяет классу указывать, что конструкторы базового класса будут унаследованы. Таким образом, компилятор C ++ 11 сгенерирует код для выполнения наследования и перенаправления производного класса базовому классу. Это функция «все или ничего»: либо все конструкторы базового класса перенаправляются, либо ни один из них не перенаправляется. Кроме того, унаследованный конструктор будет затененный если он соответствует сигнатуре конструктора производного класса, и существуют ограничения для множественного наследования: конструкторы классов не могут быть унаследован от двух классов, использующих конструкторы с одинаковой сигнатурой.

Синтаксис следующий:

учебный класс BaseClass{общественный:    BaseClass(int ценить);};учебный класс DerivedClass : общественный BaseClass{общественный:    с помощью BaseClass::BaseClass;};

Для инициализации члена C ++ 11 допускает такой синтаксис:

учебный класс SomeClass{общественный:    SomeClass() {}    явный SomeClass(int new_value) : ценить(new_value) {}частный:    int ценить = 5;};

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

Он также может использовать конструктор или унифицированную инициализацию вместо инициализации присваивания, показанной выше.

Явные переопределения и финал

В C ++ 03 возможно случайно создать новую виртуальную функцию, если она намеревалась переопределить функцию базового класса. Например:

структура Основание{    виртуальный пустота some_func(плавать);};структура Полученный : Основание{    виртуальный пустота some_func(int);};

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

C ++ 11 предоставляет синтаксис для решения этой проблемы.

структура Основание{    виртуальный пустота some_func(плавать);};структура Полученный : Основание{    виртуальный пустота some_func(int) отменять; // неправильно сформированный - не отменяет метод базового класса};

В отменять специальный идентификатор означает, что компилятор проверит базовый класс (классы), чтобы увидеть, существует ли виртуальная функция с этой точной сигнатурой. А если нет, компилятор выдаст ошибку.

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

структура Base1 окончательный { };структура Производный1 : Base1 { }; // неправильно сформированный, потому что класс Base1 был помечен как окончательный
структура Base2{    виртуальный пустота ж() окончательный;};структура Производный2 : Base2{    пустота ж(); // неправильно сформирован, потому что виртуальная функция Base2 :: f помечена как окончательная};

В этом примере виртуальная void f () final; оператор объявляет новую виртуальную функцию, но также предотвращает ее переопределение производными классами. Это также предотвращает использование производными классами этого конкретного имени функции и комбинации параметров.

Обратите внимание, что ни отменять ни окончательный ключевые слова языка. Технически они являются идентификаторами атрибутов декларатора:

  • они получают особое значение как атрибуты только при использовании в этих конкретных конечных контекстах (после всех спецификаторов типов, спецификаторов доступа, объявлений членов (для типов структур, классов и перечислений) и спецификаторов деклараторов, но перед инициализацией или реализацией кода каждого декларатора в запятой -отделенный список деклараторов);
  • они не изменяют сигнатуру объявленного типа и не объявляют и не отменяют новый идентификатор в любой области;
  • распознанные и принятые атрибуты декларатора могут быть расширены в будущих версиях C ++ (некоторые специфичные для компилятора расширения уже распознают добавленные атрибуты декларатора, чтобы предоставить компилятору параметры генерации кода или подсказки по оптимизации, или для генерации добавленных данных в скомпилированный код, предназначенных для отладчики, компоновщики и развертывание скомпилированного кода, или для предоставления дополнительных системных атрибутов безопасности, или для улучшения возможностей отражения во время выполнения, или для предоставления дополнительной информации привязки для взаимодействия с другими языками программирования и системами времени выполнения; эти расширения могут принимать параметры между скобками после идентификатора атрибута декларатора; для соответствия ANSI эти специфичные для компилятора расширения должны использовать соглашение о двойном префиксе подчеркивания).
  • В любом другом месте они могут быть действительными идентификаторами для новых объявлений (и использоваться позже, если они доступны).

Константа нулевого указателя

Для целей этого раздела и только этого раздела каждое появление «0"Означает" постоянное выражение, которое оценивается как 0, который имеет тип int ». В действительности постоянное выражение может быть любого целочисленного типа.

С момента появления C в 1972 году постоянная 0 играет двойную роль константы целого числа и константы нулевого указателя. Неоднозначность, присущая двойному значению 0 was dealt with in C by using the preprocessor macro NULL, which commonly expands to either ((void*)0) или же 0. C++ forbids implicit conversion from void * to other pointer types, thus removing the benefit of casting 0 к void *. As a consequence, only 0 is allowed as a null pointer constant. This interacts poorly with function overloading:

пустота foo(char *);пустота foo(int);

Если NULL is defined as 0 (which is usually the case in C++), the statement foo(NULL); will call foo(int), which is almost certainly not what the programmer intended, and not what a superficial reading of the code suggests.

C++11 corrects this by introducing a new keyword to serve as a distinguished null pointer constant: nullptr. It is of type nullptr_t, which is implicitly convertible and comparable to any pointer type or pointer-to-member type. It is not implicitly convertible or comparable to integral types, except for bool. While the original proposal specified that an rvalue of type nullptr_t should not be convertible to bool, the core language working group decided that such a conversion would be desirable, for consistency with regular pointer types. The proposed wording changes were unanimously voted into the Working Paper in June 2008.[2] A similar proposal is also brought to the C standard working group.[15]

For backwards compatibility reasons, 0 remains a valid null pointer constant.

char *ПК = nullptr;     // OKint  *число Пи = nullptr;     // OKbool   б = nullptr;     // OK. b is false.int    я = nullptr;     // errorfoo(nullptr);           // calls foo(nullptr_t), not foo(int);/*  Note that foo(nullptr_t) will actually call foo(char *) in the example above using an implicit conversion,  only if no other functions are overloading with compatible pointer types in scope.  If multiple overloadings exist, the resolution will fail as it is ambiguous,  unless there is an explicit declaration of foo(nullptr_t).  In standard types headers for C++11, the nullptr_t  type should be declared as:      typedef decltype(nullptr) nullptr_t;  but not as:      typedef int nullptr_t; // prior versions of C++ which need NULL to be defined as 0      typedef void *nullptr_t; // ANSI C which defines NULL as ((void*)0)*/

Strongly typed enumerations

In C++03, enumerations are not type-safe. They are effectively integers, even when the enumeration types are distinct. This allows the comparison between two enum values of different enumeration types. The only safety that C++03 provides is that an integer or a value of one enum type does not convert implicitly to another enum type. Further, the underlying integral type is implementation-defined; code that depends on the size of the enumeration is thus non-portable. Lastly, enumeration values are scoped to the enclosing scope. Thus, it is not possible for two separate enumerations in the same scope to have matching member names.

C++11 allows a special classification of enumeration that has none of these issues. This is expressed using the enum class (enum struct is also accepted as a synonym) declaration:

enum учебный класс Перечисление{    Val1,    Val2,    Val3 = 100,    Val4 // = 101};

This enumeration is type-safe. Enum class values are not implicitly converted to integers. Thus, they cannot be compared to integers either (the expression Enumeration::Val4 == 101 gives a compile error).

The underlying type of enum classes is always known. The default type is int; this can be overridden to a different integral type as can be seen in this example:

enum учебный класс Enum2 : unsigned int {Val1, Val2};

With old-style enumerations the values are placed in the outer scope. With new-style enumerations they are placed within the scope of the enum class name. So in the above example, Val1 is undefined, but Enum2::Val1 is defined.

There is also a transitional syntax to allow old-style enumerations to provide explicit scoping, and the definition of the underlying type:

enum Enum3 : unsigned long {Val1 = 1, Val2};

In this case the enumerator names are defined in the enumeration's scope (Enum3::Val1), but for backwards compatibility they are also placed in the enclosing scope.

Forward-declaring enums is also possible in C++11. Formerly, enum types could not be forward-declared because the size of the enumeration depends on the definition of its members. As long as the size of the enumeration is specified either implicitly or explicitly, it can be forward-declared:

enum Enum1;                      // Invalid in C++03 and C++11; the underlying type cannot be determined.enum Enum2 : unsigned int;       // Valid in C++11, the underlying type is specified explicitly.enum учебный класс Enum3;                // Valid in C++11, the underlying type is int.enum учебный класс Enum4 : unsigned int; // Valid in C++11.enum Enum2 : unsigned short;     // Invalid in C++11, because Enum2 was formerly declared with a different underlying type.

Right angle bracket

C++03's parser defines “>>” as the right shift operator or stream extraction operator in all cases. However, with nested template declarations, there is a tendency for the programmer to neglect to place a space between the two right angle brackets, thus causing a compiler syntax error.

C++11 improves the specification of the parser so that multiple right angle brackets will be interpreted as closing the template argument list where it is reasonable. This can be overridden by using parentheses around parameter expressions using the “>”, “>=” or “>>” binary operators:

шаблон<bool Тест> учебный класс SomeType;std::вектор<SomeType<1>2>> x1;  // Interpreted as a std::vector of SomeType,    // followed by "2 >> x1", which is not valid syntax for a declarator. 1 is true.std::вектор<SomeType<(1>2)>> x1;  // Interpreted as std::vector of SomeType,    // followed by the declarator "x1", which is valid C++11 syntax. (1>2) is false.

Explicit conversion operators

C++98 added the explicit keyword as a modifier on constructors to prevent single-argument constructors from being used as implicit type conversion operators. However, this does nothing for actual conversion operators. For example, a smart pointer class may have an operator bool() to allow it to act more like a primitive pointer: if it includes this conversion, it can be tested with if (smart_ptr_variable) (which would be true if the pointer was non-null and false otherwise). However, this allows other, unintended conversions as well. Because C++ bool is defined as an arithmetic type, it can be implicitly converted to integral or even floating-point types, which allows for mathematical operations that are not intended by the user.

In C++11, the explicit keyword can now be applied to conversion operators. As with constructors, it prevents using those conversion functions in implicit conversions. However, language contexts that specifically need a boolean value (the conditions of if-statements and loops, and operands to the logical operators) count as explicit conversions and can thus use a bool conversion operator.

For example, this feature solves cleanly the safe bool проблема.

Template aliases

In C++03, it is possible to define a typedef only as a synonym for another type, including a synonym for a template specialization with all actual template arguments specified. It is not possible to create a typedef template. Например:

шаблон <typename Первый, typename Второй, int В третьих>учебный класс SomeType;шаблон <typename Второй>typedef SomeType<OtherType, Второй, 5> TypedefName; // Invalid in C++03

This will not compile.

C++11 adds this ability with this syntax:

шаблон <typename Первый, typename Второй, int В третьих>учебный класс SomeType;шаблон <typename Второй>с помощью TypedefName = SomeType<OtherType, Второй, 5>;

В с помощью syntax can also be used as type aliasing in C++11:

typedef пустота (*FunctionType)(double);       // Old styleс помощью FunctionType = пустота (*)(double); // New introduced syntax

Unrestricted unions

In C++03, there are restrictions on what types of objects can be members of a союз. For example, unions cannot contain any objects that define a non-trivial constructor or destructor. C++11 lifts some of these restrictions.[3]

If a союз member has a non trivial special member function, the compiler will not generate the equivalent member function for the союз and it must be manually defined.

This is a simple example of a union permitted in C++11:

#include <new> // Needed for placement 'new'.struct Point{    Point() {}    Point(int Икс, int у): x_(Икс), y_(у) {}     int x_, y_;};союз U{    int z;    double ш;    Point п; // Invalid in C++03; valid in C++11.    U() {} // Due to the Point member, a constructor definition is now needed.    U(const Point& pt) : п(pt) {} // Construct Point object using initializer list.    U& оператор=(const Point& pt) { новый(&п) Point(pt); возвращаться *это; } // Assign Point object using placement 'new'.};

The changes will not break any existing code since they only relax current rules.

Core language functionality improvements

These features allow the language to do things that were formerly impossible, exceedingly verbose, or needed non-portable libraries.

Variadic templates

In C++11, templates can take variable numbers of template parameters. This also allows the definition of type-safe variadic functions.

New string literals

C++03 offers two kinds of string literals. The first kind, contained within double quotes, produces a null-terminated array of type const char. The second kind, defined as L"", produces a null-terminated array of type const wchar_t, куда wchar_t is a wide-character of undefined size and semantics. Neither literal type offers support for string literals with UTF-8, UTF-16, or any other kind of Unicode encodings.

The definition of the type char has been modified to explicitly express that it's at least the size needed to store an eight-bit coding of UTF-8, and large enough to contain any member of the compiler's basic execution character set. It was formerly defined as only the latter in the C++ standard itself, then relying on the C standard to guarantee at least 8 bits.

C++11 supports three Unicode encodings: UTF-8, UTF-16, и UTF-32. Along with the formerly noted changes to the definition of char, C++11 adds two new character types: char16_t и char32_t. These are designed to store UTF-16 and UTF-32 respectively.

Creating string literals for each of these encodings can be done thusly:

u8"I'm a UTF-8 string."ты"This is a UTF-16 string."U"This is a UTF-32 string."

The type of the first string is the usual const char[]. The type of the second string is const char16_t[] (note lower case 'u' prefix). The type of the third string is const char32_t[] (upper case 'U' prefix).

When building Unicode string literals, it is often useful to insert Unicode code points directly into the string. To do this, C++11 allows this syntax:

u8"This is a Unicode Character: u2018."ты"This is a bigger Unicode Character: u2018."U"This is a Unicode Character: U00002018."

The number after the u is a hexadecimal number; it does not need the usual 0x prefix. The identifier u represents a 16-bit Unicode code point; to enter a 32-bit code point, use U and a 32-bit hexadecimal number. Only valid Unicode code points can be entered. For example, code points on the range U+D800–U+DFFF are forbidden, as they are reserved for surrogate pairs in UTF-16 encodings.

It is also sometimes useful to avoid escaping strings manually, particularly for using literals of XML files, scripting languages, or regular expressions. C++11 provides a raw string literal:

R"(The String Data  Stuff " )"R"delimiter(The String Data  Stuff " )delimiter"

In the first case, everything between the "( и )" is part of the string. В " и \ characters do not need to be escaped. In the second case, the "delimiter( starts the string, and it ends only when )delimiter" is reached. The string delimiter can be any string up to 16 characters in length, including the empty string. This string cannot contain spaces, control characters, (, ), или \ character. Using this delimiter string allows the user to have ) characters within raw string literals. Например, R"delimiter((a-z))delimiter" is equivalent to "(a-z)".[4]

Raw string literals can be combined with the wide literal or any of the Unicode literal prefixes:

u8R"XXX(I'm a "raw UTF-8" string.)XXX"uR"*(This is a "raw UTF-16" string.)*"UR"(This is a "raw UTF-32" string.)"

User-defined literals

C++03 provides a number of literals. The characters 12.5 are a literal that is resolved by the compiler as a type double with the value of 12.5. However, the addition of the suffix ж, as in 12.5f, creates a value of type плавать that contains the value 12.5. The suffix modifiers for literals are fixed by the C++ specification, and C++03 code cannot create new literal modifiers.

By contrast, C++11 enables the user to define new kinds of literal modifiers that will construct objects based on the string of characters that the literal modifies.

Transformation of literals is redefined into two distinct phases: raw and cooked. A raw literal is a sequence of characters of some specific type, while the cooked literal is of a separate type. The C++ literal 1234, as a raw literal, is this sequence of characters '1', '2', '3', '4'. As a cooked literal, it is the integer 1234. The C++ literal 0xA in raw form is '0', 'x', 'A', while in cooked form it is the integer 10.

Literals can be extended in both raw and cooked forms, with the exception of string literals, which can be processed only in cooked form. This exception is due to the fact that strings have prefixes that affect the specific meaning and type of the characters in question.

All user-defined literals are suffixes; defining prefix literals is not possible. All suffixes starting with any character except underscore (_) are reserved by the standard. Thus, all user-defined literals must have suffixes starting with an underscore (_).[16]

User-defined literals processing the raw form of the literal are defined via a literal operator, which is written as operator "". An example follows:

OutputType оператор "" _mysuffix(const char * literal_string){    // assumes that OutputType has a constructor that takes a const char *    OutputType ret(literal_string);    возвращаться ret;}OutputType some_variable = 1234_mysuffix;// assumes that OutputType has a get_value() method that returns a doubleassert(some_variable.get_value() == 1234.0)

The assignment statement OutputType some_variable = 1234_mysuffix; executes the code defined by the user-defined literal function. This function is passed "1234" as a C-style string, so it has a null terminator.

An alternative mechanism for processing integer and floating point raw literals is via a variadic template:

шаблон<char...> OutputType оператор "" _tuffix();OutputType some_variable = 1234_tuffix;OutputType another_variable = 2.17_tuffix;

This instantiates the literal processing function as operator "" _tuffix<'1', '2', '3', '4'>(). In this form, there is no null character terminating the string. The main purpose for doing this is to use C++11's constexpr keyword to ensure that the compiler will transform the literal entirely at compile time, assuming OutputType is a constexpr-constructible and copyable type, and the literal processing function is a constexpr function.

For numeric literals, the type of the cooked literal is either unsigned long long for integral literals or long double for floating point literals. (Note: There is no need for signed integral types because a sign-prefixed literal is parsed as an expression containing the sign as a unary prefix operator and the unsigned number.) There is no alternative template form:

OutputType оператор "" _suffix(unsigned long long);OutputType оператор "" _suffix(long double);OutputType some_variable = 1234_suffix; // Uses the 'unsigned long long' overload.OutputType another_variable = 3.1416_suffix; // Uses the 'long double' overload.

In accord with the formerly mentioned new string prefixes, for string literals, these are used:

OutputType оператор "" _ssuffix(const char     * string_values, size_t num_chars);OutputType оператор "" _ssuffix(const wchar_t  * string_values, size_t num_chars);OutputType оператор "" _ssuffix(const char16_t * string_values, size_t num_chars);OutputType оператор "" _ssuffix(const char32_t * string_values, size_t num_chars);OutputType some_variable =   "1234"_ssuffix; // Uses the 'const char *' overload.OutputType some_variable = u8"1234"_ssuffix; // Uses the 'const char *' overload.OutputType some_variable =  L"1234"_ssuffix; // Uses the 'const wchar_t *'  overload.OutputType some_variable =  ты"1234"_ssuffix; // Uses the 'const char16_t *' overload.OutputType some_variable =  U"1234"_ssuffix; // Uses the 'const char32_t *' overload.

There is no alternative template form. Character literals are defined similarly.

Multithreading memory model

C++11 standardizes support for multithreaded programming.

There are two parts involved: a memory model which allows multiple threads to co-exist in a program and library support for interaction between threads. (See this article's section on threading facilities.)

The memory model defines when multiple threads may access the same memory location, and specifies when updates by one thread become visible to other threads.

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

In a multi-threaded environment, it is common for every thread to have some unique переменные. This already happens for the local variables of a function, but it does not happen for global and static variables.

A new thread-local storage duration (in addition to the existing статический, dynamic и автоматический) is indicated by the storage specifier thread_local.

Any object which could have static storage duration (i.e., lifetime spanning the entire execution of the program) may be given thread-local duration instead. The intent is that like any other static-duration variable, a thread-local object can be initialized using a constructor and destroyed using a destructor.

Explicitly defaulted and deleted special member functions

In C++03, the compiler provides, for classes that do not provide them for themselves, a default constructor, a copy constructor, a copy assignment operator (operator=), and a destructor. The programmer can override these defaults by defining custom versions. C++ also defines several global operators (such as operator new) that work on all classes, which the programmer can override.

However, there is very little control over creating these defaults. Making a class inherently non-copyable, for example, requires declaring a private copy constructor and copy assignment operator and not defining them. Attempting to use these functions is a violation of the One Definition Rule (ODR). While a diagnostic message is not required,[17] violations may result in a linker error.

In the case of the default constructor, the compiler will not generate a default constructor if a class is defined with любой constructors. This is useful in many cases, but it is also useful to be able to have both specialized constructors and the compiler-generated default.

C++11 allows the explicit defaulting and deleting of these special member functions.[18] For example, this type explicitly declares that it is using the default constructor:

struct SomeType{    SomeType() = default; //The default constructor is explicitly stated.    SomeType(OtherType ценить);};

Alternatively, certain features can be explicitly disabled. For example, this type is non-copyable:

struct NonCopyable{    NonCopyable() = default;    NonCopyable(const NonCopyable&) = Удалить;    NonCopyable& оператор=(const NonCopyable&) = Удалить;};

В = delete specifier can be used to prohibit calling any function, which can be used to disallow calling a member function with particular parameters. Например:

struct NoInt{    пустота ж(double я);    пустота ж(int) = Удалить;};

An attempt to call f() with an int will be rejected by the compiler, instead of performing a silent conversion to double. This can be generalized to disallow calling the function with any type other than double as follows:

struct OnlyDouble{    пустота ж(double d);    шаблон<учебный класс Т> пустота ж(Т) = Удалить;};

Тип long long int

In C++03, the largest integer type is long int. It is guaranteed to have at least as many usable bits as int. This resulted in long int having size of 64 bits on some popular implementations and 32 bits on others. C++11 adds a new integer type long long int to address this issue. It is guaranteed to be at least as large as a long int, and have no fewer than 64 bits. The type was originally introduced by C99 to the standard C, and most C++ compilers supported it as an extension already.[19][20]

Static assertions

C++03 provides two methods to test assertions: the macro assert and the preprocessor directive #error. However, neither is appropriate for use in templates: the macro tests the assertion at execution-time, while the preprocessor directive tests the assertion during preprocessing, which happens before instantiation of templates. Neither is appropriate for testing properties that are dependent on template parameters.

The new utility introduces a new way to test assertions at compile-time, using the new keyword static_assert.The declaration assumes this form:

static_assert (constant-expression, error-message);

Here are some examples of how static_assert can be used:

static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
шаблон<учебный класс Т>struct Check{    static_assert(sizeof(int) <= sizeof(Т), "T is not big enough!");};
шаблон<учебный класс интеграл>интеграл foo(интеграл Икс, интеграл у){    static_assert(std::is_integral<интеграл>::ценить, "foo() parameter must be an integral type.");}

When the constant expression is ложный the compiler produces an error message. The first example is similar to the preprocessor directive #error, although the preprocessor does only support integral types.[21] In contrast, in the second example the assertion is checked at every instantiation of the template class Check.

Static assertions are useful outside of templates also. For instance, a given implementation of an algorithm might depend on the size of a long long being larger than an int, something the standard does not guarantee. Such an assumption is valid on most systems and compilers, but not all.

Allow sizeof to work on members of classes without an explicit object

In C++03, the sizeof operator can be used on types and objects. But it cannot be used to do this:

struct SomeType { OtherType член; };sizeof(SomeType::член); // Does not work with C++03. Okay with C++11

This should return the size of OtherType. C++03 disallows this, so it is a compile error. C++11 allows it. It is also allowed for the alignof operator introduced in C++11.

Control and query object alignment

C++11 allows variable alignment to be queried and controlled with alignof и alignas.

В alignof operator takes the type and returns the power of 2 byte boundary on which the type instances must be allocated (as a std::size_t). When given a reference type alignof returns the referenced type's alignment; for arrays it returns the element type's alignment.

В alignas specifier controls the memory alignment for a variable. The specifier takes a constant or a type; when supplied a type alignas(T) is shorthand for alignas(alignof(T)). For example, to specify that a char array should be properly aligned to hold a float:

alignas(плавать) unsigned char c[sizeof(плавать)]

Allow garbage collected implementations

Prior C++ standards provided for programmer-driven garbage collection via set_new_handler, but gave no definition of object reachability for the purpose of automatic garbage collection. C++11 defines conditions under which pointer values are "safely derived" from other values. An implementation may specify that it operates under strict pointer safety, in which case pointers that are not derived according to these rules can become invalid.

Attributes

C++11 provides a standardized syntax for compiler/tool extensions to the language. Such extensions were traditionally specified using #pragma directive or vendor-specific keywords (like __attribute__ for GNU and __declspec for Microsoft). With the new syntax, added information can be specified in a form of an attribute enclosed in double square brackets. An attribute can be applied to various elements of source code:

int [[attr1]] я [[attr2, attr3]];[[attr4(arg1, arg2)]] если (cond){    [[продавец::attr5]] возвращаться я;}

In the example above, attribute attr1 applies to the type of variable я, attr2 и attr3 apply to the variable itself, attr4 applies to the если statement and vendor::attr5 applies to the return statement. In general (but with some exceptions), an attribute specified for a named entity is placed after the name, and before the entity otherwise, as shown above, several attributes may be listed inside one pair of double square brackets, added arguments may be provided for an attribute, and attributes may be scoped by vendor-specific attribute namespaces.

It is recommended that attributes have no language semantic meaning and do not change the sense of a program when ignored. Attributes can be useful for providing information that, for example, helps the compiler to issue better diagnostics or optimize the generated code.

C++11 provides two standard attributes itself: noreturn to specify that a function does not return, and carries_dependency to help optimizing multi-threaded code by indicating that function arguments or return value carry a dependency.[требуется разъяснение]

C++ standard library changes

A number of new features were introduced in the C++11 standard library. Many of these could have been implemented under the old standard, but some rely (to a greater or lesser extent) on new C++11 core features.

A large part of the new библиотеки was defined in the document C++ Standards Committee's Library Technical Report (called TR1), which was published in 2005. Various full and partial implementations of TR1 are currently available using the namespace std::tr1. For C++11 they were moved to namespace std. However, as TR1 features were brought into the C++11 standard library, they were upgraded where appropriate with C++11 language features that were not available in the initial TR1 version. Also, they may have been enhanced with features that were possible under C++03, but were not part of the original TR1 specification.

Upgrades to standard library components

C++11 offers a number of new language features that the currently existing standard library components can benefit from. For example, most standard library containers can benefit from Rvalue reference based move constructor support, both for quickly moving heavy containers around and for moving the contents of those containers to new memory locations. The standard library components were upgraded with new C++11 language features where appropriate. These include, but are not necessarily limited to:

  • Rvalue references and the associated move support
  • Support for the UTF-16 encoding unit, and UTF-32 encoding unit Unicode character types
  • Variadic templates (coupled with Rvalue references to allow for perfect forwarding)
  • Compile-time constant expressions
  • decltype
  • explicit conversion operators
  • Functions declared defaulted or deleted

Further, much time has passed since the prior C++ standard. Much code using the standard library has been written. This has revealed parts of the standard libraries that could use some improving. Among the many areas of improvement considered were standard library allocators. A new scope-based model of allocators was included in C++11 to supplement the prior model.

Threading facilities

While the C++03 language provides a memory model that supports threading, the primary support for actually using threading comes with the C++11 standard library.

A thread class (std::thread) is provided, which takes a function object (and an optional series of arguments to pass to it) to run in the new thread. It is possible to cause a thread to halt until another executing thread completes, providing thread joining support via the std::thread::join() member function. Access is provided, where feasible, to the underlying native thread object(s) for Платформа-specific operations by the std::thread::native_handle() member function.

For synchronization between threads, appropriate mutexes (std::mutex, std::recursive_mutex, etc.) and condition variables (std::condition_variable и std::condition_variable_any) are added to the library. These are accessible via Resource Acquisition Is Initialization (RAII) locks (std::lock_guard и std::unique_lock) and locking algorithms for easy use.

For high-performance, low-level work, communicating between threads is sometimes needed without the overhead of mutexes. This is done using atomic operations on memory locations. These can optionally specify the minimum memory visibility constraints needed for an operation. Explicit memory barriers may also be used for this purpose.

The C++11 thread library also includes futures and promises for passing asynchronous results between threads, and std::packaged_task for wrapping up a function call that can generate such an asynchronous result. The futures proposal was criticized because it lacks a way to combine futures and check for the completion of one promise inside a set of promises.[22]

Further high-level threading facilities such as thread pools have been remanded to a future C++ technical report. They are not part of C++11, but their eventual implementation is expected to be built entirely on top of the thread library features.

The new std::async facility provides a convenient method of running tasks and tying them to a std::future. The user can choose whether the task is to be run asynchronously on a separate thread or synchronously on a thread that waits for the value. By default, the implementation can choose, which provides an easy way to take advantage of hardware concurrency without oversubscription, and provides some of the advantages of a thread pool for simple usages.

Tuple types

Tuples are collections composed of heterogeneous objects of pre-arranged dimensions. A tuple can be considered a generalization of a struct's member variables.

The C++11 version of the TR1 tuple type benefited from C++11 features like variadic templates. To implement reasonably, the TR1 version required an implementation-defined maximum number of contained types, and substantial macro trickery. By contrast, the implementation of the C++11 version requires no explicit implementation-defined maximum number of types. Though compilers will have an internal maximum recursion depth for template instantiation (which is normal), the C++11 version of tuples will not expose this value to the user.

Using variadic templates, the declaration of the tuple class looks as follows:

шаблон <учебный класс ...Типы> учебный класс tuple;

An example of definition and use of the tuple type:

typedef std::tuple <int, double, long &, const char *> test_tuple;long lengthy = 12;test_tuple доказательство (18, 6.5, lengthy, "Ciao!");lengthy = std::получать<0>(доказательство);  // Assign to 'lengthy' the value 18.std::получать<3>(доказательство) = " Beautiful!";  // Modify the tuple’s fourth element.

It's possible to create the tuple доказательство without defining its contents, but only if the tuple elements' types possess default constructors. Moreover, it's possible to assign a tuple to another tuple: if the two tuples’ types are the same, each element type must possess a copy constructor; otherwise, each element type of the right-side tuple must be convertible to that of the corresponding element type of the left-side tuple or that the corresponding element type of the left-side tuple has a suitable constructor.

typedef std::tuple <int , double, нить       > tuple_1 t1;typedef std::tuple <char, short , const char * > tuple_2 t2 ('X', 2, "Hola!");t1 = t2; // Ok, first two elements can be converted,         // the third one can be constructed from a 'const char *'.

Just like std::make_pair за std::pair, there exists std::make_tuple to automatically create std::tuples using type deduction and auto helps to declare such a tuple. std::tie creates tuples of lvalue references to help unpack tuples. std :: ignore здесь тоже помогает. См. Пример:

авто записывать = стандартное::make_tuple("Хари Рам", "Нью-Дели", 3.5, 'А');стандартное::нить имя ; плавать GPA ; char оценка ;стандартное::галстук(имя, стандартное::игнорировать, GPA, оценка) = записывать ; // std :: ignore помогает отбросить название местастандартное::cout << имя << ' ' << GPA << ' ' << оценка << стандартное::конец ;

Доступны реляционные операторы (среди кортежей с одинаковым количеством элементов), и два выражения доступны для проверки характеристик кортежа (только во время компиляции):

  • std :: tuple_size :: значение возвращает количество элементов в кортеже Т,
  • std :: tuple_element :: тип возвращает тип номера объекта я кортежа Т.

Хеш-таблицы

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

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

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

Тип хеш-таблицыСвязанные ценностиЭквивалентные ключи
std :: unordered_setНетНет
std :: unordered_multisetНетда
std :: unordered_mapдаНет
std :: unordered_multimapдада

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

Эта новая функция не нуждалась в каких-либо расширениях ядра языка C ++ (хотя реализации будут использовать преимущества различных функций языка C ++ 11), только небольшое расширение заголовка <functional> и введение заголовков <unordered_set> и <unordered_map>. Никаких других изменений существующих стандартных классов не потребовалось, и это не зависит от каких-либо других расширений стандартной библиотеки.

Обычные выражения

Новая библиотека, определенная в новом заголовке <regex>, состоит из пары новых классов:

  • обычные выражения представлены экземпляром класса шаблона std :: regex;
  • вхождения представлены экземпляром класса шаблона std :: match_results.

Функция std :: regex_search используется для поиска, а для «поиска и замены» функция std :: regex_replace используется, который возвращает новую строку. std :: regex_search и std :: regex_replace взять регулярное выражение и строку и записать вхождения, найденные в структуре std :: match_results.

Вот пример использования std :: match_results:

const char *reg_esp = "[ ,.\\т\\n ;:] "; // Список символов-разделителей.// это можно сделать с помощью необработанных строковых литералов:// const char * reg_esp = R "([,.  t  n ;:])";стандартное::регулярное выражение rgx(reg_esp); // 'regex' - это экземпляр класса шаблона                         // 'basic_regex' с аргументом типа 'char'.стандартное::cmatch матч; // cmatch - это экземпляр класса шаблона                   // 'match_results' с аргументом типа 'const char *'.const char *цель = «Незримый университет - Анк-Морпорк»;// Идентифицирует все слова 'target', разделенные символами 'reg_esp'.если (стандартное::regex_search(цель, матч, rgx)){    // Если присутствуют слова, разделенные указанными символами.    const size_t п = матч.размер();    за (size_t а = 0; а < п; а++)    {        стандартное::нить ул (матч[а].первый, матч[а].второй);        стандартное::cout << ул << " п";    }}

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

Библиотека <regex> не требует ни изменения существующего заголовка (хотя он будет использовать их там, где это необходимо), ни расширения основного языка. В POSIX C, также доступны регулярные выражения C Библиотека POSIX # regex.h.

Умные указатели общего назначения

C ++ 11 предоставляет std :: unique_ptr, и улучшения std :: shared_ptr и std :: weak_ptr из TR1. std :: auto_ptr не рекомендуется.

Расширяемое средство случайных чисел

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

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

В отличие от стандарта C ранд, механизм C ++ 11 будет включать три базовых алгоритма движка генератора:

C ++ 11 также предоставляет ряд стандартных дистрибутивов:

Генератор и дистрибутивы объединены, как в этом примере:

#включают <random>#включают <functional>стандартное::uniform_int_distribution<int> распределение(0, 99);стандартное::mt19937 двигатель; // Твистер Mersenne MT19937авто генератор = стандартное::связывать(распределение, двигатель);int случайный = генератор(); // Создание однородного интеграла с вариацией от 0 до 99.int random2 = распределение(двигатель); // Сгенерировать другой образец напрямую, используя объекты распределения и движка.

Ссылка на оболочку

А обертка ссылка получается из экземпляра класса шаблона reference_wrapper. Ссылки-оболочки аналогичны обычным ссылкам (‘&’) Языка C ++. Чтобы получить ссылку на оболочку из любого объекта, шаблон функции ссылка используется (для постоянной ссылки Cref используется).

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

// Эта функция получит ссылку на параметр 'r' и увеличит ее.пустота func (int &р)  { р++; }// Шаблонная функция.шаблон<учебный класс F, учебный класс п> пустота грамм (F ж, п т)  { ж(т); }int главный(){    int я = 0;    грамм (func, я); // 'g ' создается                 // тогда 'i' не изменится.    стандартное::cout << я << стандартное::конец; // Вывод -> 0    грамм (func, стандартное::ссылка(я)); // 'g >' создается                           // тогда 'i' будет изменен.    стандартное::cout << я << стандартное::конец; // Вывод -> 1}

Эта новая утилита была добавлена ​​к существующей <utility> заголовок и не нуждался в дополнительных расширениях языка C ++.

Полиморфные оболочки для функциональных объектов

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

На примере можно прояснить его характеристики:

стандартное::функция<int (int, int)> func; // Создание оболочки с использованием                                    // шаблонный класс "функция".стандартное::плюс<int> Добавить; // 'плюс' объявлен как 'template  T plus (T, T);'                    // тогда 'add' имеет тип 'int add (int x, int y)'.func = Добавить;  // ОК - параметры и возвращаемые типы совпадают.int а = func (1, 2); // ПРИМЕЧАНИЕ: если оболочка func не ссылается ни на одну функцию,                     // генерируется исключение 'std :: bad_function_call'.стандартное::функция<bool (короткая, короткая)> func2 ;если (!func2){    // Верно, потому что 'func2' еще не назначена функция.    bool соседний(длинный Икс, длинный у);    func2 = &соседний; // ОК - параметры и возвращаемые типы можно преобразовать.    структура Тест    {        bool оператор()(короткая Икс, короткая у);    };    Тест машина;    func = стандартное::ссылка(машина); // 'std :: ref' - это шаблонная функция, возвращающая оболочку                          // функции-члена 'operator ()' структуры 'car'.}func = func2; // ОК - параметры и возвращаемые типы можно преобразовать.

Класс шаблона функция был определен внутри заголовка <functional>, без каких-либо изменений в языке C ++.

Типовые черты для метапрограммирования

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

Вот пример метапрограммы, использующей стандарт C ++ 03: a рекурсия экземпляров шаблона для вычисления целочисленных показателей:

шаблон<int B, int N>структура Pow{    // рекурсивный вызов и рекомбинация.    перечислить{ ценить = B*Pow<B, N-1>::ценить };};шаблон< int B >структура Pow<B, 0>{    // '' N == 0 '' условие завершения.    перечислить{ ценить = 1 };};int quartic_of_three = Pow<3, 4>::ценить;

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

Типовые черты может идентифицировать категорию объекта и все характеристики класса (или структуры). Они определены в новом заголовке <type_traits>.

В следующем примере есть шаблонная функция «разработать», которая, в зависимости от заданных типов данных, будет создавать один из двух предложенных алгоритмов (алгоритм.do_it).

// Первый способ работы.шаблон< bool B > структура Алгоритм{    шаблон<учебный класс Т1, учебный класс Т2> статический int сделай это (Т1 &, Т2 &)  { /*...*/ }};// Второй способ работы.шаблон<> структура Алгоритм<истинный>{    шаблон<учебный класс Т1, учебный класс Т2> статический int сделай это (Т1, Т2)  { /*...*/ }};// Создание экземпляра «тщательно продуманного» автоматически установит правильный способ работы.шаблон<учебный класс Т1, учебный класс Т2>int разрабатывать (Т1 А, Т2 B){    // Используйте второй способ, только если 'T1' - целое число, а 'T2' -    // с плавающей запятой, иначе воспользуйтесь первым способом.    возвращаться Алгоритм<стандартное::is_integral<Т1>::ценить && стандартное::is_floating_point<Т2>::ценить>::сделай это( А, B ) ;}

Через типовые черты, определенный в заголовке <type_traits>, также можно создавать операции преобразования типов (static_cast и const_cast недостаточны внутри шаблона).

Этот тип программирования дает элегантный и лаконичный код; однако слабым местом этих методов является отладка: неудобно во время компиляции и очень сложно во время выполнения программы.

Единый метод вычисления возвращаемого типа объектов функции

Определение типа возвращаемого значения объекта функции шаблона во время компиляции не является интуитивным, особенно если возвращаемое значение зависит от параметров функции. В качестве примера:

структура Прозрачный{    int    оператор()(int) const;    // Тип параметра -    двойной оператор()(двойной) const; // равно возвращаемому типу.};шаблон <учебный класс Obj>учебный класс Исчисление{общественный:    шаблон<учебный класс Arg> Arg оператор()(Arg& а) const    {        возвращаться член(а);    }частный:    Obj член;};

Создание экземпляра шаблона класса Исчисление <Очистить>, функциональный объект исчисление всегда будет иметь тот же тип возвращаемого значения, что и функциональный объект Прозрачный. Однако, учитывая класс Смущенный ниже:

структура Смущенный{    двойной оператор()(int) const;     // Тип параметра не    int    оператор()(двойной) const;  // равно возвращаемому типу.};

Попытка создать экземпляр Исчисление <в замешательстве> вызовет возвращаемый тип Исчисление не быть таким же, как класс Смущенный. Компилятор может выдавать предупреждения о преобразовании из int к двойной наоборот.

TR1 представляет, а C ++ 11 принимает шаблонный класс std :: result_of что позволяет определять и использовать возвращаемый тип объекта функции для каждого объявления. Предмет CalculusVer2 использует std :: result_of объект для получения возвращаемого типа объекта функции:

шаблон< учебный класс Obj >учебный класс CalculusVer2{общественный:    шаблон<учебный класс Arg>    typename стандартное::Результат<Obj(Arg)>::тип оператор()(Arg& а) const    {        возвращаться член(а);    }частный:    Obj член;};

Таким образом, в экземплярах функционального объекта CalculusVer2 <Запутался> нет преобразований, предупреждений или ошибок.

Единственное отличие от версии TR1 std :: result_of в том, что версия TR1 позволяла реализации не иметь возможности определять тип результата вызова функции. В связи с изменениями в C ++ для поддержки decltype, версия C ++ 11 std :: result_of больше не нужны эти особые случаи; реализации требуются для вычисления типа во всех случаях.

Улучшенная совместимость с C

Для совместимости с C, из C99 были добавлены:[23]

  • Препроцессор:[24]
    • вариативные макросы,
    • конкатенация смежных узких / широких строковых литералов,
    • _Pragma () - эквивалент #pragma.
  • долго долго - целочисленный тип длиной не менее 64 бита.
  • __func__ - макрос, вычисляющий имя функции, в которой он находится.
  • Заголовки:
    • cstdbool (stdbool.h),
    • cstdint (stdint.h),
    • cinttypes (inttypes.h).

Функции изначально запланированы, но удалены или не включены

Заголовок для отдельного ТУ:

  • Модули
  • Десятичные типы
  • Специальные математические функции

Перенесенный:

  • Концепции
  • Более полная или необходимая поддержка сборки мусора
  • Отражение
  • Объемы макросов

Функции удалены или устарели

Период, термин точка последовательности был удален, заменен указанием, что либо одна операция выполняется в последовательности перед другой, либо две операции не выполняются.[25]

Прежнее использование ключевого слова экспорт был удален.[26] Само ключевое слово остается, зарезервировано для потенциального использования в будущем.

Динамический спецификации исключения устарели.[26] Спецификация во время компиляции функций, не вызывающих исключения, доступна с нет кроме ключевое слово, которое полезно для оптимизации.

std :: auto_ptr устарел, будучи заменен std :: unique_ptr.

Базовые классы функциональных объектов (std :: unary_function, std :: binary_function), адаптеры указателей на функции и адаптеры указателей на члены, а также классы связывания устарели.

Смотрите также

Рекомендации

  1. ^ «У нас есть международный стандарт: C ++ 0x единогласно одобрен». Получено 12 августа 2011.
  2. ^ Саттер, Херб (18 августа 2014 г.), У нас есть C ++ 14!, получено 2014-08-18
  3. ^ Страуструп, Бьярн. «C ++ 11 FAQ». stroustrup.com.
  4. ^ «Обзор C ++ 11: какими конкретными целями проектирования руководствовался комитет?». Стандартный C ++.
  5. ^ «Бьярн Страуструп: обзор C ++ 0x» (PDF). Получено 30 июн 2011.
  6. ^ «ISO / IEC 14882: 2011». ISO. 2 сентября 2011 г.. Получено 3 сентября 2011.
  7. ^ «Рабочий проект стандарта языка программирования C ++» (PDF).
  8. ^ "Стандарт". Получено 2012-11-02.
  9. ^ Саттер, Александреску "Стандарты кодирования C ++" №15
  10. ^ Габриэль дос Рейс; Бьярне Страуструп (22 марта 2010 г.). "Общие константные выражения для языков системного программирования, Труды SAC '10" (PDF).
  11. ^ Яакко Ярви; Бьярне Страуструп; Дуглас Грегор; Джереми Сик (28 апреля 2003 г.). «Decltype и auto, язык программирования C ++, документ №: N1478 = 03-0061» (PDF).
  12. ^ Роджер Орр (июнь 2013 г.). ""Авто - необходимое зло? "Журнал перегрузки № 115".
  13. ^ «Документ №: N1968 = 06-0038- Лямбда-выражения и замыкания для C ++» (PDF). Открытые стандарты.
  14. ^ "автоматический указатель (начиная с C ++ 11) - cppreference.com". en.cppreference.com.
  15. ^ Густедт, Йенс (2019-07-09). "Введите константу nullptr - v1" (PDF). Реестр документов ISO JTC1 / SC22 / WG14. Международная организация по стандартизации - через open-std.org.
  16. ^ Это вызвало конфликт с предлагаемым использованием (распространенным в других языках) подчеркивания для группировка цифр в числовых литералах, таких как целочисленные литералы, так C ++ 14 вместо этого использует апостроф (как верхняя запятая) для группировки.Дэвид Вандевурд (21 сентября 2012 г.). «N3448: Безболезненное разделение цифр» (PDF)., Лоуренс Кроул (19 декабря 2012 г.). «N3499: Разделители цифр».
  17. ^ ISO/IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §3.2 Одно правило определения [basic.def.odr] пункт 3
  18. ^ «Функции по умолчанию и удаленные функции - ISO / IEC JTC1 SC22 WG21 N2210 = 07-0070 - 2007-03-11».
  19. ^ «Использование коллекции компиляторов GNU (GCC): Long Long». gcc.gnu.org.
  20. ^ Диапазоны типов данных (C ++)
  21. ^ Сэмюэл П. Харбисон III, Гай Л. Стил-младший: "C - Справочное руководство", 5-е издание, стр. 251
  22. ^ Милевски, Бартош (3 марта 2009 г.). «Нарушенные обещания - фьючерсы на C ++ 0x». Получено 24 января 2010.
  23. ^ "Clang - Статус C ++ 98, C ++ 11 и C ++ 14". Clang.llvm.org. 2013-05-12. Получено 2013-06-10.
  24. ^ «Рабочий проект изменений для синхронизации препроцессора C99». www.open-std.org.
  25. ^ Пещеры, Джонатан (4 июня 2007 г.). «Обновление стандарта языка C ++ - 0x». Получено 25 мая 2010.
  26. ^ а б Саттер, Херб (3 марта 2010 г.). «Отчет о поездке: встреча по стандартам ISO C ++ за март 2010 г.». Получено 24 марта 2010.

внешняя ссылка