С ++ 11 - C++11

C ++ 11 - это версия стандарта ISO / IEC 14882 для языка программирования C ++ . C ++ 11 заменил предыдущую версию стандарта C ++, названную C ++ 03 , а позже был заменен на C ++ 14 . Название следует традиции именования языковых версий по году публикации спецификации, хотя ранее оно называлось C ++ 0x, поскольку ожидалось, что оно будет опубликовано до 2010 года.

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

C ++ 11 был опубликован как ISO / IEC 14882: 2011 в сентябре 2011 года и доступен за плату. Рабочий проект, наиболее похожий на опубликованный стандарт C ++ 11, - это N3337 от 16 января 2012 г .; в нем есть только редакционные исправления из стандарта C ++ 11.

Цели дизайна

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

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

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

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

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

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

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

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

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

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

В C ++ 11 шаг конструктор изstd::vector<T>который принимает ссылкуRvalue Аняstd::vector<T>можно скопировать указатель на внутренний массив C-стиле из RValue в новыйstd::vector<T>,затем установить указатель внутри RValue в нуль. Поскольку временное значение больше никогда не будет использоваться, никакой код не будет пытаться получить доступ к нулевому указателю, а поскольку указатель имеет значение NULL, его память не удаляется, когда он выходит за пределы области видимости. Следовательно, операция не только не требует затрат на глубокое копирование, но и является безопасной и невидимой.

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

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

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

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

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

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

int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++

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

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

constexpr int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Valid C++11

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

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

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

constexpr double earth_gravitational_acceleration = 9.8;
constexpr double 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вместо использования конструктора копирования. Время жизни тривиального типа начинается с момента определения его хранилища, а не с момента завершения конструктора.

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

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

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

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

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

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

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

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

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

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

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

template class std::vector<MyClass>;

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

extern template class std::vector<MyClass>;

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

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

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

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

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

struct Object
{
    float first;
    int second;
};

Object scalar = {0.43f, 10}; //One Object, with first=0.43f and second=10
Object anArray[] = {{13.4f, 3}, {43.28f, 29}, {5.934f, 17}}; //An array of three Objects

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

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

class SequenceClass
{
public:
    SequenceClass(std::initializer_list<int> list);
};

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

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

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

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

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

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

void function_name(std::initializer_list<float> list); // Copying is cheap; see above

function_name({1.0f, -3.45f, -0.4f});

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

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

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v({ "xyzzy", "plugh", "abracadabra" });
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; // see "Uniform initialization" below

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

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

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

struct BasicStruct
{
    int x;
    double y;
};

struct AltStruct
{
    AltStruct(int x, double y)
        : x_{x}
        , y_{y}
    {}

private:
    int x_;
    double y_;
};

BasicStruct var1{5, 3.2};
AltStruct var2{2, 4.3};

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

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

struct IdString
{
    std::string name;
    int identifier;
};

IdString get_string()
{
    return {"foo", 42}; //Note the lack of explicit type.
}

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

std::vector<int> the_vec{4};

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

Вывод типа

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

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

auto some_strange_callable_type = std::bind(&some_function, _2, _1, some_object);
auto other_variable = 5;

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

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

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

int some_int;
decltype(some_int) other_integer_variable = 5;

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

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

for (std::vector<int>::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr)

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

for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr)

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

for (const auto& x : myvec)

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

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

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a has type int
    decltype(v[0]) b = 1; // b has type const int&, the return type of
                          //   std::vector<int>::operator[](size_type) const
    auto c = 0;           // c has type int
    auto d = c;           // d has type int
    decltype(c) e;        // e has type int, the type of the entity named by c
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue
    decltype(0) g;        // g has type int, because 0 is an rvalue
}

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

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

int my_array[5] = {1, 2, 3, 4, 5};
// double the value of each element in my_array:
for (int& x : my_array)
    x *= 2;

// similar but also using type inference for array elements
for (auto& x : my_array)
    x *= 2;

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

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

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

[](int x, int y) -> int { return x + y; }

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

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

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

template<class Lhs, class Rhs>
  Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret must be the type of lhs+rhs

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

template<class Lhs, class Rhs>
  decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Not valid C++11

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

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

template<class Lhs, class Rhs>
  auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

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

struct SomeStruct
{
    auto func_name(int x, int y) -> int;
};

auto SomeStruct::func_name(int x, int y) -> int
{
    return x + y;
}

Использование ключевого слова «auto» в этом случае является лишь частью синтаксиса и не выполняет автоматический вывод типа в C ++ 11. Однако, начиная с C ++ 14, конечный тип возвращаемого значения можно полностью удалить, и компилятор автоматически определит тип возвращаемого значения.

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

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

class SomeType
{
public:
    SomeType(int new_number)
    {
        Construct(new_number);
    }

    SomeType()
    {
        Construct(42);
    }

private:
    void Construct(int new_number)
    {
        number = new_number;
    }

    int number;
};

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

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

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

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

class SomeType
{
    int number;

public:
    SomeType(int new_number) : number(new_number) {}
    SomeType() : SomeType(42) {}
};

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

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

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

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

class BaseClass
{
public:
    BaseClass(int value);
};

class DerivedClass : public BaseClass
{
public:
    using BaseClass::BaseClass;
};

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

class SomeClass
{
public:
    SomeClass() {}
    explicit SomeClass(int new_value) : value(new_value) {}

private:
    int value = 5;
};

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

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

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

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

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int);
};

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

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

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

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

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

struct Base1 final { };

struct Derived1 : Base1 { }; // ill-formed because the class Base1 has been marked final
struct Base2
{
    virtual void f() final;
};

struct Derived2 : Base2
{
    void f(); // ill-formed because the virtual function Base2::f has been marked final
};

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

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

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

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

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

С момента появления C в 1972 году константа 0играла двойную роль: константа целого числа и константа нулевого указателя. Неоднозначность, присущая двойному значению слова, 0была устранена в C с помощью макроса препроцессора NULL, который обычно расширяется до ((void*)0)или 0. C ++ запрещает неявное преобразование из void *в другие типы указателей, тем самым устраняя преимущество преобразования 0в void *. Как следствие, 0допускается только константа нулевого указателя. Это плохо взаимодействует с перегрузкой функций :

void foo(char *);
void foo(int);

Если NULLопределено как 0(что обычно имеет место в C ++), оператор foo(NULL);вызовет foo(int), что почти наверняка не то, что задумал программист, и не то, что подсказывает поверхностное прочтение кода.

C ++ 11 исправляет это, вводя новое ключевое слово , чтобы служить в качестве отмеченной постоянной нулевой указатель: nullptr. Это тип nullptr_t, который неявно преобразуется и сравним с любым типом указателя или типом указателя на член. Он не может быть неявно преобразован или сопоставим с целочисленными типами, за исключением bool. В то время как исходное предложение указывало, что rvalue типа nullptr_tне должно быть преобразовано bool, рабочая группа по основному языку решила, что такое преобразование было бы желательным для согласованности с обычными типами указателей. Предложенные изменения формулировки были единогласно внесены в Рабочий документ в июне 2008 года. Аналогичное предложение также внесено в рабочую группу по стандарту C.

По причинам обратной совместимости 0остается допустимой константой нулевого указателя.

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b is false.
int    i = nullptr;     // error

foo(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)
*/

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

В C ++ 03 перечисления небезопасны по типу. По сути, это целые числа, даже если типы перечисления различны. Это позволяет сравнивать два значения перечисления разных типов перечисления. Единственная безопасность, которую обеспечивает C ++ 03, заключается в том, что целое число или значение одного типа перечисления не преобразуется неявно в другой тип перечисления. Кроме того, базовый интегральный тип определяется реализацией; код, который зависит от размера перечисления, таким образом, непереносим. Наконец, значения перечисления ограничены охватывающей областью. Таким образом, два отдельных перечисления в одной области не могут иметь совпадающие имена членов.

C ++ 11 допускает специальную классификацию перечислений, в которой нет ни одной из этих проблем. Это выражается с помощью объявления enum class( enum structтакже принимаемого как синоним):

enum class Enumeration
{
    Val1,
    Val2,
    Val3 = 100,
    Val4 // = 101
};

Это перечисление типобезопасно. Значения класса Enum не конвертируются в целые числа неявно. Таким образом, их тоже нельзя сравнивать с целыми числами (выражение Enumeration::Val4 == 101дает ошибку компиляции).

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

enum class Enum2 : unsigned int {Val1, Val2};

В перечислениях в старом стиле значения помещаются во внешнюю область видимости. В перечислениях нового стиля они помещаются в область действия имени класса перечисления. Итак, в приведенном выше примере Val1не определено, но Enum2::Val1определено.

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

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

В этом случае имена перечислителя определены в области видимости перечисления ( Enum3::Val1), но для обратной совместимости они также помещаются во включающую область.

Перечисления с прямым объявлением также возможны в C ++ 11. Раньше типы перечисления не могли быть объявлены вперед, потому что размер перечисления зависит от определения его членов. Если размер перечисления указан явно или неявно, его можно объявить вперед:

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 class Enum3;                // Valid in C++11, the underlying type is int.
enum class 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.

Правая угловая скобка

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

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

template<bool Test> class SomeType;
std::vector<SomeType<1>2>> x1;  // Interpreted as a std::vector of SomeType<true>,
    // followed by "2 >> x1", which is not valid syntax for a declarator. 1 is true.
std::vector<SomeType<(1>2)>> x1;  // Interpreted as std::vector of SomeType<false>,
    // followed by the declarator "x1", which is valid C++11 syntax. (1>2) is false.

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

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

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

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

Псевдонимы шаблонов

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

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
typedef SomeType<OtherType, Second, 5> TypedefName; // Invalid in C++03

Это не будет компилироваться.

C ++ 11 добавляет эту возможность с помощью следующего синтаксиса:

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
using TypedefName = SomeType<OtherType, Second, 5>;

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

typedef void (*FunctionType)(double);       // Old style
using FunctionType = void (*)(double); // New introduced syntax

Неограниченные союзы

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

Если unionчлен имеет нетривиальную специальную функцию-член , компилятор не будет генерировать эквивалентную функцию-член для, unionи ее необходимо определить вручную.

Это простой пример объединения, разрешенного в C ++ 11:

#include <new> // Needed for placement 'new'.

struct Point
{
    Point() {}
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_;
};

union U
{
    int z;
    double w;
    Point p; // Invalid in C++03; valid in C++11.
    U() {} // Due to the Point member, a constructor definition is now needed.
    U(const Point& pt) : p(pt) {} // Construct Point object using initializer list.
    U& operator=(const Point& pt) { new(&p) Point(pt); return *this; } // Assign Point object using placement 'new'.
};

Изменения не нарушат какой-либо существующий код, поскольку они только ослабят текущие правила.

Улучшения основных языковых функций

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

Вариативные шаблоны

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

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

C ++ 03 предлагает два типа строковых литералов . Первый тип, заключенный в двойные кавычки, создает массив типа с завершающим нулем const char. Второй тип, определенный как L"", создает массив типа с завершающим нулем const wchar_t, где wchar_t- широкий символ неопределенного размера и семантики. Ни один из типов литералов не поддерживает строковые литералы с UTF-8 , UTF-16 или любые другие типы кодировок Unicode .

Определение типа charбыло изменено, чтобы явно указать, что это, по крайней мере, размер, необходимый для хранения восьмибитного кода UTF-8 , и достаточно большой, чтобы содержать любой член базового набора символов выполнения компилятора. Раньше он определялся как последний в самом стандарте C ++, а затем полагался на стандарт C, чтобы гарантировать не менее 8 бит.

C ++ 11 поддерживает три кодировки Unicode: UTF-8 , UTF-16 и UTF-32 . Наряду с ранее отмеченными изменениями в определении charC ++ 11 добавляет два новых типа символов: char16_tи char32_t. Они предназначены для хранения UTF-16 и UTF-32 соответственно.

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

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

Тип первой строки - обычный const char[]. Тип второй строки const char16_t[](обратите внимание на префикс «u» в нижнем регистре). Тип третьей строки - const char32_t[](префикс U в верхнем регистре).

При построении строковых литералов Unicode часто бывает полезно вставлять кодовые точки Unicode непосредственно в строку. Для этого C ++ 11 допускает такой синтаксис:

u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \U00002018."

Число после \uшестнадцатеричного числа; ему не нужен обычный 0xпрефикс. Идентификатор \uпредставляет собой 16-битную кодовую точку Unicode; для ввода 32-битной кодовой точки используйте \Uи 32-битное шестнадцатеричное число. Могут быть введены только действительные кодовые точки Unicode. Например, кодовые точки в диапазоне U + D800 – U + DFFF запрещены, поскольку они зарезервированы для суррогатных пар в кодировках UTF-16.

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

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

В первом случае все между "(и )"является частью строки. В "и \символы не должны быть экранированы. Во втором случае "delimiter(строка начинает строку и заканчивается только )delimiter"по достижении. Строка delimiterможет быть любой строкой длиной до 16 символов, включая пустую строку. Эта строка не может содержать пробелы, управляющие символы, (, ), или \характер. Использование этой строки-разделителя позволяет пользователю иметь )символы в необработанных строковых литералах. Например, R"delimiter((a-z))delimiter"эквивалентно "(a-z)".

Необработанные строковые литералы можно комбинировать с широким литералом или любым из буквальных префиксов Unicode:

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.)"

Пользовательские литералы

C ++ 03 предоставляет ряд литералов. Символы 12.5- это литерал, который компилятор разрешает как тип doubleсо значением 12,5. Однако добавление суффикса f, например 12.5f, создает значение типа float, содержащее значение 12,5. Модификаторы суффиксов для литералов фиксируются спецификацией C ++, и код C ++ 03 не может создавать новые модификаторы литералов.

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

Преобразование литералов разделено на две отдельные фазы: сырую и приготовленную. Необработанный литерал - это последовательность символов определенного типа, а готовый литерал - отдельного типа. C ++ буквальным 1234, в качестве исходного буквальным, эта последовательность символов '1', '2', '3', '4'. В приготовленном буквальном, это целое числе 1234. C ++ буквальные 0xAв сыром виде '0', 'x', 'A', в то время как в вареном виде она представляет собой целое число 10.

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

Все определяемые пользователем литералы являются суффиксами; определение префиксных литералов невозможно. Все суффиксы, начинающиеся с любого символа, кроме подчеркивания ( _), зарезервированы стандартом. Таким образом, все определяемые пользователем литералы должны иметь суффиксы, начинающиеся с символа подчеркивания ( _).

Определяемые пользователем литералы, обрабатывающие необработанную форму литерала, определяются с помощью литерального оператора, который записывается как operator "". Пример ниже:

OutputType operator "" _mysuffix(const char * literal_string)
{
    // assumes that OutputType has a constructor that takes a const char *
    OutputType ret(literal_string);
    return ret;
}

OutputType some_variable = 1234_mysuffix;
// assumes that OutputType has a get_value() method that returns a double
assert(some_variable.get_value() == 1234.0)

Оператор присваивания OutputType some_variable = 1234_mysuffix;выполняет код, определенный пользовательской литеральной функцией. Эта функция передается "1234"как строка в стиле C, поэтому она имеет нулевой терминатор.

Альтернативный механизм обработки необработанных литералов с целыми числами и с плавающей запятой - с помощью вариативного шаблона :

template<char...> OutputType operator "" _tuffix();

OutputType some_variable = 1234_tuffix;
OutputType another_variable = 2.17_tuffix;

Это создает экземпляр функции буквальной обработки как operator "" _tuffix<'1', '2', '3', '4'>(). В этой форме нет нулевого символа в конце строки. Основная цель для этого - использовать constexprключевое слово C ++ 11, чтобы гарантировать, что компилятор полностью преобразует литерал во время компиляции, предполагая, что OutputTypeэто constexpr-конструктивный и копируемый тип, а функция обработки литерала является constexprфункцией.

Для числовых литералов тип подготовленного литерала - это либо unsigned long longцелочисленные литералы, либо литералы long doubleс плавающей запятой. (Примечание: нет необходимости в целочисленных типах со знаком, поскольку литерал с префиксом знака анализируется как выражение, содержащее знак как оператор унарного префикса и число без знака.) Альтернативной формы шаблона нет:

OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _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.

В соответствии с ранее упомянутыми новыми строковыми префиксами для строковых литералов используются:

OutputType operator "" _ssuffix(const char     * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const wchar_t  * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _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 =  u"1234"_ssuffix; // Uses the 'const char16_t *' overload.
OutputType some_variable =  U"1234"_ssuffix; // Uses the 'const char32_t *' overload.

Альтернативной формы шаблона нет. Символьные литералы определяются аналогично.

Модель многопоточной памяти

C ++ 11 стандартизирует поддержку многопоточного программирования .

Здесь задействованы две части: модель памяти, которая позволяет нескольким потокам сосуществовать в программе, и поддержка библиотеки для взаимодействия между потоками. (См. Раздел этой статьи, посвященный средствам многопоточности .)

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

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

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

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

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

Явно заданные по умолчанию и удаленные специальные функции-члены

В C ++ 03 компилятор предоставляет для классов, которые не предоставляют их для себя, конструктор по умолчанию, конструктор копирования, оператор присваивания копии ( operator=) и деструктор. Программист может изменить эти значения по умолчанию, определив собственные версии. C ++ также определяет несколько глобальных операторов (таких как operator new), которые работают со всеми классами, которые программист может переопределить.

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

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

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

struct SomeType
{
    SomeType() = default; //The default constructor is explicitly stated.
    SomeType(OtherType value);
};

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

struct NonCopyable
{
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

Спецификатор = deleteможет использоваться для запрета вызова любой функции, что может использоваться для запрета вызова функции-члена с определенными параметрами. Например:

struct NoInt
{
    void f(double i);
    void f(int) = delete;
};

Попытка вызвать f()с помощью intбудет отклонена компилятором вместо выполнения тихого преобразования в double. Это можно обобщить, чтобы запретить вызов функции с любым типом, кроме doubleследующего:

struct OnlyDouble
{
    void f(double d);
    template<class T> void f(T) = delete;
};

Тип long long int

В C ++ 03 самый большой целочисленный тип - это long int. Гарантируется, что у него будет как минимум столько же используемых битов, сколько int. Это привело к тому, long intчто в некоторых популярных реализациях размер был 64 бита, а в других - 32 бита. C ++ 11 добавляет новый целочисленный тип long long intдля решения этой проблемы. Гарантируется, что он будет long intне меньше, чем a , и иметь не менее 64 бит. Тип был первоначально введен C99 в стандарт C, и большинство компиляторов C ++ уже поддерживали его как расширение.

Статические утверждения

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

Новая утилита представляет новый способ проверки утверждений во время компиляции с использованием ключевого слова new static_assert. Объявление принимает такую ​​форму:

static_assert (constant-expression, error-message);

Вот несколько примеров того, как static_assertможно использовать:

static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
template<class T>
struct Check
{
    static_assert(sizeof(int) <= sizeof(T), "T is not big enough!");
};
template<class Integral>
Integral foo(Integral x, Integral y)
{
    static_assert(std::is_integral<Integral>::value, "foo() parameter must be an integral type.");
}

Когда постоянным выражением является выражение, falseкомпилятор выдает сообщение об ошибке. Первый пример похож на директиву препроцессора #error, хотя препроцессор поддерживает только целые типы. Напротив, во втором примере утверждение проверяется при каждом создании экземпляра класса шаблона Check.

Статические утверждения полезны и вне шаблонов. Например, конкретная реализация алгоритма может зависеть от размера объекта long long, большего, чем размер int, что стандарт не гарантирует. Такое предположение справедливо для большинства систем и компиляторов, но не для всех.

Разрешить sizeofработать с членами классов без явного объекта

В C ++ 03 sizeofоператор можно использовать для типов и объектов. Но его нельзя использовать для этого:

struct SomeType { OtherType member; };

sizeof(SomeType::member); // Does not work with C++03. Okay with C++11

Это должно вернуть размер OtherType. C ++ 03 запрещает это, поэтому это ошибка компиляции. C ++ 11 позволяет это. Это также разрешено для alignofоператора, представленного в C ++ 11.

Выравнивание объекта управления и запроса

C ++ 11 позволяет запрашивать и контролировать выравнивание переменных с помощью alignofи alignas.

alignofОператор принимает тип и возвращает мощность 2 границы байта , на котором должны быть выделены экземпляры типа (как std::size_t). Когда указан ссылочный тип, alignofвозвращает выравнивание ссылочного типа; для массивов возвращает выравнивание типа элемента.

Спецификатор alignasуправляет выравниванием памяти для переменной. Спецификатор принимает константу или тип; при поставке тип alignas(T)является сокращением для alignas(alignof(T)). Например, чтобы указать, что массив символов должен быть правильно выровнен для хранения числа с плавающей запятой:

alignas(float) unsigned char c[sizeof(float)]

Разрешить реализации со сборкой мусора

Предыдущие стандарты C ++ предусматривали сборку мусора, управляемую программистами set_new_handler, но не давали определения достижимости объекта с целью автоматической сборки мусора. C ++ 11 определяет условия, при которых значения указателя «безопасно выводятся» из других значений. Реализация может указывать, что она работает со строгой безопасностью указателей , и в этом случае указатели, которые не получены в соответствии с этими правилами, могут стать недействительными.

Атрибуты

C ++ 11 предоставляет стандартизированный синтаксис для расширений компилятора / инструментальных средств языка. Такие расширения традиционно указывались с помощью #pragmaдиректив или ключевых слов производителя (например, __attribute__для GNU и __declspecдля Microsoft). С новым синтаксисом добавленная информация может быть указана в форме атрибута, заключенного в двойные квадратные скобки. Атрибут может применяться к различным элементам исходного кода:

int [[attr1]] i [[attr2, attr3]];

[[attr4(arg1, arg2)]] if (cond)
{
    [[vendor::attr5]] return i;
}

В приведенном выше примере, атрибут attr1относится к типу переменных i, attr2и attr3применить к самому переменному, attr4относится к ifзаявлению и vendor::attr5относится к возвратному заявлению. В общем (но с некоторыми исключениями) атрибут, указанный для именованной сущности, помещается после имени и перед сущностью, в противном случае, как показано выше, несколько атрибутов могут быть перечислены в одной паре двойных квадратных скобок, могут быть предоставлены дополнительные аргументы. для атрибута, а атрибуты могут быть охвачены пространством имен атрибутов, зависящим от поставщика.

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

C ++ 11 сам предоставляет два стандартных атрибута: noreturnчтобы указать, что функция не возвращает, и carries_dependencyдля помощи в оптимизации многопоточного кода, указав, что аргументы функции или возвращаемое значение несут зависимость.

Изменения стандартной библиотеки C ++

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

Большая часть новых библиотек была определена в документе Технический отчет о библиотеках Комитета по стандартам C ++ (называемый TR1), который был опубликован в 2005 году. В настоящее время доступны различные полные и частичные реализации TR1 с использованием пространства имен std::tr1. Для C ++ 11 они были перемещены в пространство имен std. Однако, поскольку функции TR1 были добавлены в стандартную библиотеку C ++ 11, они были обновлены там, где это необходимо, с помощью функций языка C ++ 11, которые не были доступны в начальной версии TR1. Кроме того, они могли быть расширены функциями, которые были возможны в C ++ 03, но не были частью исходной спецификации TR1.

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

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

  • Ссылки Rvalue и соответствующая поддержка перемещения
  • Поддержка блока кодирования UTF-16 и блока кодирования UTF-32 типов символов Unicode
  • Шаблоны Variadic (в сочетании со ссылками Rvalue для обеспечения идеальной пересылки)
  • Выражения констант времени компиляции
  • decltype
  • explicit операторы преобразования
  • Функции объявлены дефолтными или удалены

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

Средства потоковой передачи

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

Предоставляется класс потока ( std::thread), который принимает объект функции (и необязательную серию аргументов для передачи ему) для запуска в новом потоке. Можно вызвать остановку потока до тех пор, пока другой выполняющийся поток не завершится, обеспечивая поддержку присоединения потока через std::thread::join()функцию-член. Доступ обеспечивается, когда это возможно, к нижележащему родной резьбы объекта (ов) для платформы -специфические операций по std::thread::native_handle()функции члена.

Для синхронизации между потоками в библиотеку добавляются соответствующие мьютексы ( std::mutex, std::recursive_mutexи т. Д.) И условные переменные ( std::condition_variableи std::condition_variable_any). Они доступны через блокировки ( и ) и алгоритмы блокировки для получения ресурсов (RAII) . std::lock_guardstd::unique_lock

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

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

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

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

Типы кортежей

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

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

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

template <class ...Types> class tuple;

Пример определения и использования типа кортеж:

typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");

lengthy = std::get<0>(proof);  // Assign to 'lengthy' the value 18.
std::get<3>(proof) = " Beautiful!";  // Modify the tuple’s fourth element.

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

typedef std::tuple <int , double, string       > 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 *'.

Как и std::make_pairдля std::pair, существует std::make_tupleвозможность автоматического создания std::tuples с использованием вывода типа и autoпомогает объявить такой кортеж. std::tieсоздает кортежи ссылок lvalue, чтобы помочь распаковать кортежи. std::ignoreздесь тоже помогает. См. Пример:

auto record = std::make_tuple("Hari Ram", "New Delhi", 3.5, 'A');
std::string name ; float gpa ; char grade ;
std::tie(name, std::ignore, gpa, grade) = record ; // std::ignore helps drop the place name
std::cout << name << ' ' << gpa << ' ' << grade << std::endl ;

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

  • std::tuple_size<T>::valueвозвращает количество элементов в кортеже T,
  • std::tuple_element<I, T>::typeвозвращает тип номера объекта Iкортежа T.

Хеш-таблицы

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

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

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

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

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

Эта новая функция не нуждалась в каких-либо расширениях ядра языка 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 = "[ ,.\\t\\n;:]"; // List of separator characters.

// this can be done using raw string literals:
// const char *reg_esp = R"([ ,.\t\n;:])";

std::regex rgx(reg_esp); // 'regex' is an instance of the template class
                         // 'basic_regex' with argument of type 'char'.
std::cmatch match; // 'cmatch' is an instance of the template class
                   // 'match_results' with argument of type 'const char *'.
const char *target = "Unseen University - Ankh-Morpork";

// Identifies all words of 'target' separated by characters of 'reg_esp'.
if (std::regex_search(target, match, rgx))
{
    // If words separated by specified characters are present.

    const size_t n = match.size();
    for (size_t a = 0; a < n; a++)
    {
        std::string str (match[a].first, match[a].second);
        std::cout << str << "\n";
    }
}

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

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

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

С ++ 11 предоставляет std::unique_ptrи усовершенствования std::shared_ptrи std::weak_ptrот TR1. std::auto_ptrустарела.

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

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

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

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

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

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

#include <random>
#include <functional>

std::uniform_int_distribution<int> distribution(0, 99);
std::mt19937 engine; // Mersenne twister MT19937
auto generator = std::bind(distribution, engine);
int random = generator(); // Generate a uniform integral variate between 0 and 99.
int random2 = distribution(engine); // Generate another sample directly using the distribution and the engine objects.

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

Обертка ссылка получена из экземпляра класса шаблона reference_wrapper. Ссылки-оболочки аналогичны обычным ссылкам (' &') языка C ++. Для получения ссылки на оболочку от любого объекта используется шаблон функции ref(для постоянной ссылки cref).

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

// This function will obtain a reference to the parameter 'r' and increment it.
void func (int &r)  { r++; }

// Template function.
template<class F, class P> void g (F f, P t)  { f(t); }

int main()
{
    int i = 0;
    g (func, i); // 'g<void (int &r), int>' is instantiated
                 // then 'i' will not be modified.
    std::cout << i << std::endl; // Output -> 0

    g (func, std::ref(i)); // 'g<void(int &r),reference_wrapper<int>>' is instantiated
                           // then 'i' will be modified.
    std::cout << i << std::endl; // Output -> 1
}

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

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

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

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

std::function<int (int, int)> func; // Wrapper creation using
                                    // template class 'function'.
std::plus<int> add; // 'plus' is declared as 'template<class T> T plus( T, T ) ;'
                    // then 'add' is type 'int add( int x, int y )'.
func = add;  // OK - Parameters and return types are the same.

int a = func (1, 2); // NOTE: if the wrapper 'func' does not refer to any function,
                     // the exception 'std::bad_function_call' is thrown.

std::function<bool (short, short)> func2 ;
if (!func2)
{
    // True because 'func2' has not yet been assigned a function.

    bool adjacent(long x, long y);
    func2 = &adjacent; // OK - Parameters and return types are convertible.

    struct Test
    {
        bool operator()(short x, short y);
    };
    Test car;
    func = std::ref(car); // 'std::ref' is a template function that returns the wrapper
                          // of member function 'operator()' of struct 'car'.
}
func = func2; // OK - Parameters and return types are convertible.

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

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

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

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

template<int B, int N>
struct Pow
{
    // recursive call and recombination.
    enum{ value = B*Pow<B, N-1>::value };
};

template< int B >
struct Pow<B, 0>
{
    // ''N == 0'' condition of termination.
    enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;

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

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

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

// First way of operating.
template< bool B > struct Algorithm
{
    template<class T1, class T2> static int do_it (T1 &, T2 &)  { /*...*/ }
};

// Second way of operating.
template<> struct Algorithm<true>
{
    template<class T1, class T2> static int do_it (T1, T2)  { /*...*/ }
};

// Instantiating 'elaborate' will automatically instantiate the correct way to operate.
template<class T1, class T2>
int elaborate (T1 A, T2 B)
{
    // Use the second way only if 'T1' is an integer and if 'T2' is
    // in floating point, otherwise use the first way.
    return Algorithm<std::is_integral<T1>::value && std::is_floating_point<T2>::value>::do_it( A, B ) ;
}

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

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

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

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

struct Clear
{
    int    operator()(int) const;    // The parameter type is
    double operator()(double) const; // equal to the return type.
};

template <class Obj>
class Calculus
{
public:
    template<class Arg> Arg operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

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

struct Confused
{
    double operator()(int) const;     // The parameter type is not
    int    operator()(double) const;  // equal to the return type.
};

Попытка создать экземпляр Calculus<Confused>приведет к тому, что тип возвращаемого значения Calculusне будет таким же, как у класса Confused. Компилятор может выдавать предупреждения о преобразовании из intв doubleи наоборот.

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

template< class Obj >
class CalculusVer2
{
public:
    template<class Arg>
    typename std::result_of<Obj(Arg)>::type operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

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

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

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

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

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

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

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

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

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

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

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

Термин « точка последовательности» был удален и заменен указанием, что либо одна операция выполняется в последовательности, а затем другая, либо две операции не выполняются.

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

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

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

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

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

использованная литература

внешние ссылки