С++11
В этой статье может быть слишком много заголовков разделов . ( Март 2017 г. ) |
Эта статья может быть слишком технической для понимания большинства читателей . ( Март 2017 г. ) |
Версии языка C++ |
---|
C++11 — это версия стандарта ISO / IEC 14882 для языка программирования C++ . C++11 заменил предыдущую версию стандарта C++, называемую C++03 . [ 1 ] и позже был заменен C++14 . Название соответствует традиции именования языковых версий по году публикации спецификации, хотя раньше она называлась C++0x, поскольку ожидалось, что она будет опубликована до 2010 года. [ 2 ]
Хотя одной из целей проектирования было отдать предпочтение изменениям в библиотеках, а не изменениям в основном языке , [ 3 ] В C++11 есть несколько дополнений к основному языку. Области основного языка, которые были значительно улучшены, включают поддержку многопоточности, поддержку общего программирования , унифицированную инициализацию и производительность. Значительные изменения были также внесены в Стандартную библиотеку C++ , включившую в себя большинство C++ Технического отчета 1 (TR1) библиотек , за исключением библиотеки специальных математических функций. [ 4 ]
C++11 был опубликован как ISO/IEC 14882:2011. [ 5 ] в сентябре 2011 года и доступен за определенную плату. Рабочий проект, наиболее похожий на опубликованный стандарт C++11, — N3337 от 16 января 2012 г.; [ 6 ] в нем есть только редакционные исправления стандарта C++11. [ 7 ]
C++11 полностью поддерживается Clang 3.3 и более поздних версий. [ 8 ] C++11 полностью поддерживается GCC 4.8.1 и более поздних версий. [ 9 ]
Цели дизайна
[ редактировать ]Комитет по проектированию пытался достичь ряда целей при разработке C++11:
- Поддерживать стабильность и совместимость со старым кодом.
- Предпочитаете вводить новые функции через стандартную библиотеку, а не расширять основной язык.
- Улучшите C++, чтобы облегчить проектирование систем и библиотек, а не вводите новые функции, полезные только для конкретных приложений.
- Повышайте типобезопасность, предоставляя более безопасные альтернативы более ранним небезопасным методам.
- Повышение производительности и возможность работы напрямую с оборудованием
- Предоставление правильных решений реальных проблем
- Сделайте C++ простым в обучении и обучении, не удаляя при этом какие-либо утилиты, необходимые опытным программистам.
Внимание к новичкам считается важным, потому что большинство программистов всегда будут таковыми, а также потому, что многие новички никогда не расширяют свои знания, ограничиваясь работой над аспектами языка, на котором они специализируются. [ 2 ] [ 3 ]
Расширения основного языка C++.
[ редактировать ]Одной из функций комитета C++ является разработка ядра языка. Области основного языка, которые были значительно улучшены, включают многопоточности поддержку , поддержку общего программирования , унифицированную инициализацию и производительность.
Улучшения производительности среды выполнения основного языка
[ редактировать ]Эти языковые функции в первую очередь существуют для того, чтобы обеспечить некоторый выигрыш в производительности либо в отношении памяти, либо в скорости вычислений. [ нужна ссылка ]
Ссылки Rvalue и конструкторы перемещения
[ редактировать ]В C++03 (и ранее) временные значения (называемые « rvalue », поскольку они часто лежат в правой части присваивания) никогда не могли быть изменены — как и в C — и считались неотличимыми от const T&
типы; тем не менее, в некоторых случаях временные объекты могли быть изменены, и такое поведение даже считалось полезной лазейкой. [ 10 ] В 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. Поскольку временный объект больше никогда не будет использоваться, никакой код не будет пытаться получить доступ к нулевому указателю, а поскольку указатель имеет значение NULL, его память не удаляется, когда он выходит за пределы области действия. Следовательно, операция не только не требует затрат на глубокую копию, но и является безопасной и невидимой.
Ссылки Rvalue могут повысить производительность существующего кода без необходимости внесения каких-либо изменений за пределы стандартной библиотеки. Тип возвращаемого значения функции, возвращающей std::vector<T>
временный не нужно явно менять на std::vector<T> &&
для вызова конструктора перемещения, поскольку временные значения автоматически считаются значениями r. (Однако, если std::vector<T>
является версией C++03 без конструктора перемещения, то конструктор копирования будет вызываться с помощью const std::vector<T>&
, что требует значительного выделения памяти.)
В целях безопасности наложены некоторые ограничения. Именованная переменная никогда не будет считаться rvalue, даже если она объявлена как таковая. Чтобы получить значение r, шаблон функции 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
, что позволяет пользователю гарантировать, что функция или конструктор объекта являются константой времени компиляции. [ 11 ] Приведенный выше пример можно переписать следующим образом:
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 значения переменных можно было использовать в константных выражениях только в том случае, если переменные объявлены const, имеют инициализатор, который является постоянным выражением, и имеют целочисленный тип или тип перечисления. 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
, вместо того, чтобы использовать конструктор копирования. Жизнь тривиального типа начинается с момента определения его хранилища, а не с момента завершения конструктора.
Тривиальный класс или структура определяется как тот, который:
- Имеет тривиальный конструктор по умолчанию. При этом может использоваться синтаксис конструктора по умолчанию (
SomeConstructor() = default;
). - Имеет тривиальные конструкторы копирования и перемещения, которые могут использовать синтаксис по умолчанию.
- Имеет тривиальные операторы копирования и перемещения, которые могут использовать синтаксис по умолчанию.
- Имеет тривиальный деструктор, который не должен быть виртуальным.
Конструкторы тривиальны только в том случае, если у класса нет виртуальных функций-членов и виртуальных базовых классов. Операции копирования/перемещения также требуют, чтобы все нестатические элементы данных были тривиальными.
Тип со стандартной компоновкой означает, что он упорядочивает и упаковывает свои члены способом, совместимым с C. Класс или структура имеют стандартную компоновку по определению при условии:
- У него нет виртуальных функций.
- У него нет виртуальных базовых классов.
- Все его нестатические элементы данных имеют одинаковый контроль доступа (общедоступный, частный, защищенный).
- Все его нестатические элементы данных, включая любые элементы базовых классов, находятся в одном классе в иерархии.
- Вышеупомянутые правила также применяются ко всем базовым классам и ко всем нестатическим членам данных в иерархии классов.
- Он не имеет базовых классов того же типа, что и первый определенный нестатический элемент данных.
Класс/структура/объединение считается POD, если он тривиален, имеет стандартную структуру и все его нестатические элементы данных и базовые классы являются POD.
Разделив эти понятия, становится возможным отказаться от одного, не потеряв при этом другого. Класс со сложными конструкторами перемещения и копирования может быть нетривиальным, но он может иметь стандартную компоновку и, таким образом, взаимодействовать с C. Аналогичным образом, класс с общедоступными и частными нестатическими элементами данных не будет иметь стандартной компоновки, но может быть тривиально и поэтому memcpy
-способный.
Улучшения производительности во время сборки основного языка
[ редактировать ]Внешний шаблон
[ редактировать ]В C++03 компилятор должен создавать экземпляр шаблона всякий раз, когда в единице перевода встречается полностью заданный шаблон. Если шаблон создается с одинаковыми типами во многих единицах трансляции, это может значительно увеличить время компиляции. В C++03 это невозможно предотвратить, поэтому в C++11 введены объявления шаблонов 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}
(и так далее для других разновидностей строительного синтаксиса).
Список можно скопировать после его создания, что обходится дешевле и будет действовать как копирование по ссылке (класс обычно реализуется как пара указателей начала/конца). Ан std::initializer_list
является постоянным: его члены не могут быть изменены после его создания, а также не могут быть изменены данные в этих членах (что исключает перемещение из них, необходимость копирования в члены класса и т. д.).
Хотя его конструкция специально обрабатывается компилятором, std::initializer_list
является реальным типом, поэтому его можно использовать не только в конструкторах классов, но и в других местах. Обычные функции могут принимать типизированные std::initializer_list
в качестве аргументов. Например:
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
s числового типа.
Стандартные контейнеры также можно инициализировать следующими способами:
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
ключевое слово. [ 12 ] [ 13 ] Это создает переменную определенного типа инициализатора:
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
, поскольку тип переменной 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)
Эта разница возрастает по мере того, как программист начинает вкладывать контейнеры, хотя в таких случаях typedef
s — хороший способ уменьшить объем кода.
Тип, обозначаемый 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
, называемый «for на основе диапазона», будет перебирать каждый элемент в списке. Он будет работать для массивов в стиле C, списков инициализаторов и любого типа, который имеет begin()
и end()
определенные для него функции, которые возвращают итераторы. Все контейнеры стандартной библиотеки, имеющие пары начало/конец, будут работать с оператором for на основе диапазона.
Лямбда-функции и выражения
[ редактировать ]C++11 предоставляет возможность создавать анонимные функции , называемые лямбда-функциями. [ 14 ] Они определяются следующим образом:
[](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 введен новый синтаксис объявления функции с конечным типом возврата : [ 15 ]
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, завершающий тип возвращаемого значения можно полностью удалить, и компилятор определит тип возвращаемого значения автоматически. [ 16 ]
Улучшение конструкции объекта
[ редактировать ]В 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;
};
Любой конструктор класса инициализирует value
с 5, если конструктор не переопределяет инициализацию своей собственной. Таким образом, приведенный выше пустой конструктор инициализируется 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
};
The 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
являются ключевыми словами языка. Технически они являются идентификаторами атрибутов декларатора:
- они приобретают особое значение как атрибуты только при использовании в этих конкретных конечных контекстах (после всех спецификаторов типа, спецификаторов доступа, объявлений членов (для типов struct, class и enum) и спецификаторов деклараторов, но перед инициализацией или реализацией кода каждого декларатора в запятой. -отдельный список деклараторов);
- они не изменяют сигнатуру объявленного типа и не объявляют и не переопределяют какой-либо новый идентификатор ни в какой области действия;
- распознанные и принятые атрибуты декларатора могут быть расширены в будущих версиях 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
. Хотя в исходном предложении указано, что значение типа nullptr_t
не должен быть конвертирован в bool
, основная рабочая группа по языку решила, что такое преобразование желательно для обеспечения согласованности с обычными типами указателей. Предложенные изменения формулировок были единогласно включены в рабочий документ в июне 2008 года. [1] Аналогичное предложение также было внесено в рабочую группу по стандарту C и было принято для включения в C23 . [ 17 ]
По соображениям обратной совместимости, 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)
(что было бы верно, если бы указатель был ненулевым, и ложным в противном случае). Однако это допускает и другие, непреднамеренные преобразования. Потому что С++ bool
определяется как арифметический тип, его можно неявно преобразовать в целочисленные типы или даже типы с плавающей запятой, что позволяет выполнять математические операции, не предназначенные для пользователя.
В С++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>;
The using
синтаксис также можно использовать в качестве псевдонимов типов в C++11:
typedef void (*FunctionType)(double); // Old style
using FunctionType = void (*)(double); // New introduced syntax
Неограниченные профсоюзы
[ редактировать ]В C++03 существуют ограничения на то, какие типы объектов могут быть членами union
. Например, объединения не могут содержать объекты, определяющие нетривиальный конструктор или деструктор. C++11 снимает некоторые из этих ограничений. [2]
Если 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 кодировки .
C++11 поддерживает три кодировки Unicode: UTF-8, UTF-16 и UTF-32 . Определение типа char
был изменен, чтобы явно указать, что его размер как минимум соответствует размеру, необходимому для хранения восьмибитной кодировки UTF-8, и достаточно велик, чтобы содержать любой член базового набора символов выполнения компилятора. Раньше в самом стандарте C++ он определялся только как последний, а затем полагался на стандарт C, гарантирующий не менее 8 бит. Кроме того, в C++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» в верхнем регистре).
При построении строковых литералов Юникода часто бывает полезно вставлять кодовые точки Юникода непосредственно в строку. Для этого 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-битную кодовую точку Юникода; чтобы ввести 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)\""
.
Необработанные строковые литералы можно комбинировать с расширенными литералами или любыми префиксами литералов Юникода:
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.
Литералы могут быть расширены как в сырой, так и в готовой форме, за исключением строковых литералов, которые могут обрабатываться только в готовой форме. Это исключение связано с тем, что строки имеют префиксы, влияющие на конкретное значение и тип рассматриваемых символов.
Все определяемые пользователем литералы являются суффиксами; определение префиксных литералов невозможно. Все суффиксы, начинающиеся с любого символа, кроме подчеркивания ( _
) зарезервированы стандартом. Таким образом, все определяемые пользователем литералы должны иметь суффиксы, начинающиеся с подчеркивания ( _
). [ 18 ]
Пользовательские литералы, обрабатывающие необработанную форму литерала, определяются с помощью литерального оператора, который записывается как 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'>()
. В этой форме нет нулевого символа, завершающего строку. Основная цель этого — использовать C++11. constexpr
ключевое слово, чтобы гарантировать, что компилятор полностью преобразует литерал во время компиляции, предполагая 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). Хотя диагностическое сообщение не требуется, [ 19 ] нарушения могут привести к ошибке компоновщика.
В случае конструктора по умолчанию компилятор не будет генерировать конструктор по умолчанию, если класс определен с помощью каких-либо конструкторов. Это полезно во многих случаях, но также полезно иметь возможность иметь как специализированные конструкторы, так и значения по умолчанию, генерируемые компилятором.
C++11 позволяет явно устанавливать значения по умолчанию и удалять эти специальные функции-члены. [ 20 ] Например, этот класс явно объявляет, что можно использовать конструктор по умолчанию:
class SomeType
{
SomeType() = default; //The default constructor is explicitly stated.
SomeType(OtherType value);
};
Явно удаленные функции
[ редактировать ]Функцию можно явно отключить. Это полезно для предотвращения неявных преобразований типов.
= delete
Спецификатор можно использовать для запрета вызова функции с определенными типами параметров. [ 20 ] Например:
void noInt(double i);
void noInt(int) = delete;
Попытка позвонить noInt()
с int
параметр будет отклонен компилятором вместо выполнения автоматического преобразования в double
. Вызов noInt()
с float
все еще работает.
Можно запретить вызов функции любого типа, кроме double
с помощью шаблона:
double onlyDouble(double d) {return d;}
template<typename T> double onlyDouble(T) = delete;
звоню onlyDouble(1.0)
будет работать, пока onlyDouble(1.0f)
выдаст ошибку компилятора.
Функции и конструкторы-члены класса также можно удалить. Например, можно предотвратить копирование объектов класса, удалив конструктор копирования и operator =
:
class NonCopyable
{
NonCopyable();
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
Тип long long int
[ редактировать ] В C++03 самым большим целочисленным типом является long int
. Гарантируется, что он будет иметь по крайней мере столько же полезных битов, сколько int
. Это привело к long int
имеющий размер 64 бита в некоторых популярных реализациях и 32 бита в других. В C++11 добавлен новый целочисленный тип. long long int
для решения этой проблемы. Гарантированно он будет не меньше размера long int
и иметь не менее 64 бит. Этот тип был первоначально введен C99 в стандарт C, и большинство компиляторов C++ уже поддерживали его как расширение. [ 21 ] [ 22 ]
Статические утверждения
[ редактировать ]C++03 предоставляет два метода проверки утверждений : макрос assert
и директива препроцессора #error
. Однако ни один из них не подходит для использования в шаблонах: макрос проверяет утверждение во время выполнения, а директива препроцессора проверяет утверждение во время предварительной обработки, которая происходит до создания экземпляров шаблонов. Ни один из них не подходит для тестирования свойств, которые зависят от параметров шаблона.
Новая утилита представляет новый способ проверки утверждений во время компиляции с использованием нового ключевого слова 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
, хотя препроцессор поддерживает только целочисленные типы. [ 23 ] Напротив, во втором примере утверждение проверяется при каждом экземпляре класса шаблона. 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 запрещает это, поэтому это ошибка компиляции. С++ 11 позволяет это. Допускается также для alignof
оператор, представленный в C++11.
Выравнивание объектов управления и запроса
[ редактировать ]C++11 позволяет запрашивать и контролировать выравнивание переменных с помощью alignof
и alignas
.
The alignof
принимает тип и возвращает степень двухбайтовой границы, на которой должны быть размещены экземпляры типа (как std::size_t
). Когда задан ссылочный тип alignof
возвращает выравнивание ссылочного типа; для массивов он возвращает выравнивание типа элемента.
The 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
для Майкрософт). Благодаря новому синтаксису добавленная информация может быть указана в форме атрибута, заключенного в двойные квадратные скобки. Атрибут можно применять к различным элементам исходного кода:
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. Типы символов Юникода.
- Шаблоны 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
) добавляются в библиотеку. Доступ к ним осуществляется через блокировки Resource Acquisition Is Initialization (RAII) ( std::lock_guard
и std::unique_lock
) и алгоритмы блокировки для простоты использования.
Для высокопроизводительной низкоуровневой работы иногда требуется взаимодействие между потоками без использования мьютексов. Это делается с помощью атомарных операций над ячейками памяти. Они могут дополнительно указывать минимальные ограничения видимости памяти, необходимые для операции. явные барьеры памяти Для этой цели также можно использовать .
Библиотека потоков C++11 также включает фьючерсы и обещания для передачи асинхронных результатов между потоками и std::packaged_task
для завершения вызова функции, которая может генерировать такой асинхронный результат. Предложение о фьючерсах подверглось критике, поскольку в нем отсутствует способ объединить фьючерсы и проверить выполнение одного обещания внутри набора обещаний. [ 24 ]
Дальнейшие средства многопоточности высокого уровня, такие как пулы потоков, будут перенесены в будущий технический отчет 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::tuple
с использованием вывода типа и 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».
Новая библиотека имеет четыре типа хеш-таблиц, различающихся тем, принимают ли они элементы с одним и тем же ключом (уникальные ключи или эквивалентные ключи) и сопоставляют ли они каждый ключ соответствующему значению. Они соответствуют четырем существующим ассоциативным контейнерам на основе двоичного дерева поиска с 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>
. Никаких других изменений в существующих стандартных классах не потребовалось, и это не зависит от каких-либо других расширений стандартной библиотеки.
std::array и std::forward_list
[ редактировать ]Помимо хеш-таблиц в стандартную библиотеку добавлено еще два контейнера. Std::array — это контейнер фиксированного размера, который более эффективен, чем std::vector, но безопаснее и проще в использовании, чем массив c-стиля. Std::forward_list — это односвязный список, который обеспечивает более эффективное хранение данных, чем двунаправленный список std::list, когда двунаправленная итерация не требуется.
Регулярные выражения
[ редактировать ]Новая библиотека, определенная в новом заголовке <regex>
, состоит из пары новых классов:
- регулярные выражения представлены экземпляром класса шаблона
std::regex
; - вхождения представлены экземпляром класса шаблона
std::match_results
, - std::regex_iterator используется для перебора всех совпадений регулярного выражения.
Функция std::regex_search
используется для поиска, а для поиска и замены используется функция std::regex_replace
используется, который возвращает новую строку. [ 25 ]
Вот пример использования std::regex_iterator
:
#include <regex>
const char *pattern = R"([^ ,.\t\n]+)"; // find words separated by space, comma, period tab newline
std::regex rgx(pattern); // throws exception on invalid pattern
const char *target = "Unseen University - Ankh-Morpork";
// Use a regex_iterator to identify all words of 'target' separated by characters of 'pattern'.
auto iter =
std::cregex_iterator(target, target + strlen(target), rgx);
// make an end of sequence iterator
auto end =
std::cregex_iterator();
for (; iter != end; ++iter)
{
std::string match_str = iter->str();
std::cout << match_str << '\n';
}
Библиотека <regex>
не требует ни изменения какого-либо существующего заголовка (хотя он будет использовать их там, где это необходимо), ни расширения основного языка. В POSIX C регулярные выражения также доступны через библиотеку C POSIX#regex.h .
Интеллектуальные указатели общего назначения
[ редактировать ]C++11 предоставляет std::unique_ptr
и улучшения std::shared_ptr
и std::weak_ptr
от ТР1. std::auto_ptr
устарел.
Расширяемая возможность случайных чисел
[ редактировать ]Стандартная библиотека C предоставляет возможность генерировать псевдослучайные числа с помощью функции rand
. Однако алгоритм полностью делегируется поставщику библиотеки. C++ унаследовал эту функциональность без изменений, но C++11 предоставляет новый метод генерации псевдослучайных чисел.
Функциональность случайных чисел C++11 разделена на две части: механизм генератора, который содержит состояние генератора случайных чисел и выдает псевдослучайные числа; и распределение, которое определяет диапазон и математическое распределение результата. Эти два объединяются для формирования объекта генератора случайных чисел.
В отличие от стандарта C rand
механизм C++11 будет включать три базовых алгоритма генератора:
C++11 также предоставляет ряд стандартных дистрибутивов:
uniform_int_distribution
,uniform_real_distribution
,bernoulli_distribution
,binomial_distribution
,geometric_distribution
,negative_binomial_distribution
,poisson_distribution
,exponential_distribution
,gamma_distribution
,weibull_distribution
,extreme_value_distribution
,normal_distribution
,lognormal_distribution
,chi_squared_distribution
,cauchy_distribution
,fisher_f_distribution
,student_t_distribution
,discrete_distribution
,piecewise_constant_distribution
иpiecewise_linear_distribution
.
Генератор и распределения объединяются, как в этом примере:
#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 take 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
}
Эта новая утилита была добавлена к существующей <functional>
заголовок и не нуждался в дальнейших расширениях языка 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++.
Type traits for metaprogramming
[ редактировать ]Метапрограммирование заключается в создании программы, которая создает или модифицирует другую программу (или саму себя). Это может произойти во время компиляции или во время выполнения. Комитет по стандартизации C++ решил ввести библиотеку для метапрограммирования при компиляции через шаблоны.
Вот пример метапрограммы, использующей стандарт 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>
.
В следующем примере есть шаблонная функция «elaborate», которая, в зависимости от заданных типов данных, создаст экземпляр одного из двух предложенных алгоритмов ( 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 были добавлены: [ 26 ]
- Препроцессор: [ 27 ]
- вариативные макросы ,
- конкатенация соседних узких/широких строковых литералов,
_Pragma()
– эквивалент#pragma
.
long long
– целочисленный тип длиной не менее 64 бит.__func__
– макрос, вычисляющий имя функции, в которой он находится.- Заголовки:
cstdbool
(stdbool.h
),cstdint
(stdint.h
),cinttypes
(inttypes.h
).
Функции, изначально запланированные, но удаленные или не включенные
[ редактировать ]Заголовок для отдельного ТР:
- Модули
- Десятичные типы
- Специальные математические функции
Перенесенный:
- Концепции
- Более полная или необходимая поддержка сборки мусора
- Отражение
- Макрообъемы
Функции удалены или устарели
[ редактировать ]Термин «точка последовательности» был удален и заменен указанием, что либо одна операция упорядочивается перед другой, либо две операции не упорядочиваются. [ 28 ]
Прежнее использование ключевого слова export
был удален. [ 29 ] Само ключевое слово остается зарезервированным для потенциального использования в будущем.
Спецификации динамических исключений устарели. [ 29 ] Спецификация времени компиляции функций, не генерирующих исключения, доступна с помощью noexcept
ключевое слово, которое полезно для оптимизации.
std::auto_ptr
устарел и был заменен на std::unique_ptr
.
Базовые классы объектов функций ( std::unary_function
, std::binary_function
), адаптеры для указателей на функции и адаптеры для указателей на члены, а также классы связывания устарели.
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «У нас есть международный стандарт: C++0x одобрен единогласно» . 12 августа 2011 г. Архивировано из оригинала 11 декабря 2018 г. Проверено 12 августа 2011 г.
- ^ Перейти обратно: а б Страуструп, Бьерн. «Часто задаваемые вопросы по C++11» . stroustrup.com . Архивировано из оригинала 06.10.2018 . Проверено 15 октября 2014 г.
- ^ Перейти обратно: а б «Обзор C++11: какими конкретными целями проектирования руководствовался комитет?» . Стандартный С++ . Архивировано из оригинала 31 января 2019 г. Проверено 4 сентября 2015 г.
- ^ «Бьерн Страуструп: обзор C++0x» (PDF) . Архивировано (PDF) из оригинала 17 июня 2016 года . Проверено 30 июня 2011 г.
- ^ «ИСО/МЭК 14882:2011» . ИСО. 2 сентября 2011 г. Архивировано из оригинала 29 января 2013 г. Проверено 3 сентября 2011 г.
- ^ «Рабочий проект стандарта языка программирования C++» (PDF) . Архивировано (PDF) из оригинала 21 января 2019 г. Проверено 26 апреля 2012 г.
- ^ «Стандарт» . Архивировано из оригинала 13 мая 2019 г. Проверено 2 ноября 2012 г.
- ^ «Clang — состояние языка программирования C++» . 2023-11-29. Архивировано из оригинала 29 ноября 2023 г. Проверено 1 декабря 2023 г.
- ^ «Выпущен GCC 4.8.1, реализована функция C++11: стандарт C++» . isocpp.org . Проверено 1 декабря 2023 г.
- ^ Саттер, Александреску «Стандарты кодирования C++» № 15
- ^ Габриэль Дос Рейс; Бьерн Страуструп (22 марта 2010 г.). «Общие константные выражения для языков системного программирования, Труды SAC '10» (PDF) . Архивировано (PDF) из оригинала 13 июня 2018 года . Проверено 18 августа 2012 г.
- ^ Яакко Ярви; Бьерн Страуструп; Дуглас Грегор; Джереми Сик (28 апреля 2003 г.). «Decltype и auto, язык программирования C++, документ №: N1478=03-0061» (PDF) . Архивировано (PDF) из оригинала 28 мая 2015 г. Проверено 6 июня 2015 г.
- ^ Роджер Орр (июнь 2013 г.). « Авто – необходимое зло?» Журнал «Перегрузка №115» . Архивировано из оригинала 6 июня 2015 г. Проверено 6 июня 2015 г.
- ^ «Документ №: N1968=06-0038-Лямбда-выражения и замыкания для C++» (PDF) . Открытые стандарты. Архивировано (PDF) из оригинала 28 июля 2011 г. Проверено 20 апреля 2009 г.
- ^ «Decltype (редакция 5)» (PDF) . Архивировано (PDF) из оригинала 14 февраля 2022 г. Проверено 16 февраля 2022 г.
- ^ «автоматический спецификатор (начиная с C++11) — cppreference.com» . ru.cppreference.com . Архивировано из оригинала 20 октября 2016 г. Проверено 18 октября 2016 г.
- ^ Густедт, Йенс (9 июля 2019 г.). «Введение константы nullptr — v1» (PDF) . Реестр документов ISO JTC1/SC22/WG14 . Международная организация по стандартизации. Архивировано (PDF) из оригинала 27 июля 2020 г. Проверено 19 апреля 2020 г. - через open-std.org.
- ^ Это вызвало конфликт с предлагаемым использованием (распространенным в других языках) подчеркивания для группировки цифр в числовых литералах, таких как целочисленные литералы , поэтому C++14 вместо этого использует апостроф (в качестве верхней запятой ) для группировки. Дэвид Вандевурде (21 сентября 2012 г.). «N3448: Безболезненное разделение цифр» (PDF) . Архивировано (PDF) из оригинала 11 августа 2015 г. Проверено 13 августа 2015 г. , Лоуренс Кроул (19 декабря 2012 г.). «N3499: Разделители цифр» . Архивировано из оригинала 11 августа 2015 г. Проверено 13 августа 2015 г.
- ^ ИСО / МЭК (2003). ISO/IEC 14882 :2003(E): Языки программирования – C++ §3.2 Одно правило определения [basic.def.odr], параграф. 3
- ^ Перейти обратно: а б «Функции по умолчанию и удаленные функции – ISO/IEC JTC1 SC22 WG21 N2210 = 07-0070 – 11 марта 2007 г.» . Архивировано из оригинала 19 августа 2012 г. Проверено 20 декабря 2012 г.
- ^ «Использование коллекции компиляторов GNU (GCC): Long Long» . gcc.gnu.org . Архивировано из оригинала 21 августа 2016 г. Проверено 25 июля 2016 г.
- ^ «Диапазоны типов данных (C++)» . Архивировано из оригинала 21 февраля 2009 г. Проверено 23 апреля 2009 г.
- ^ Сэмюэл П. Харбисон III, Гай Л. Стил младший: «C – Справочное руководство», 5-е издание, стр.251
- ^ Милевский, Бартош (3 марта 2009 г.). «Нарушенные обещания – фьючерсы на C++0x» . Архивировано из оригинала 16 сентября 2011 года . Проверено 24 января 2010 г.
- ^ «Библиотека регулярных выражений C++» . cppreference.com . Проверено 10 декабря 2022 г.
- ^ «Clang — статус C++98, C++11 и C++14» . Clang.llvm.org. 12 мая 2013 г. Архивировано из оригинала 28 мая 2019 г. Проверено 10 июня 2013 г.
- ^ «Рабочий проект изменений для синхронизации препроцессора C99» . www.open-std.org . Архивировано из оригинала 31 июля 2020 г. Проверено 26 мая 2014 г.
- ^ Кейвс, Джонатан (4 июня 2007 г.). «Обновление стандарта языка C++-0x» . Архивировано из оригинала 9 сентября 2011 года . Проверено 25 мая 2010 г.
- ^ Перейти обратно: а б Саттер, Херб (3 марта 2010 г.). «Отчет о поездке: собрание по стандартам ISO C++, март 2010 г.» . Архивировано из оригинала 11 июля 2018 года . Проверено 24 марта 2010 г.
Внешние ссылки
[ редактировать ] в этом разделе Использование внешних ссылок может не соответствовать политике и рекомендациям Википедии . ( Октябрь 2018 г. ) |
- Комитет по стандартам C++
- C++0X: новое лицо стандарта C++
- Освещение C++11 в блоге Херба Саттера
- Освещение C++11 в блоге Энтони Уильямса
- Доклад о C++0x, сделанный Бьярном Страуструпом в Университете Ватерлоо. Архивировано 23 января 2009 г. в Wayback Machine.
- Состояние языка: интервью с Бьярном Страуструпом (15 августа 2008 г.). Архивировано 31 января 2009 г. на Wayback Machine.
- Wiki-страница, помогающая отслеживать основные функции языка C++ 0x и их доступность в компиляторах.
- Интернет-ссылка на стандартную библиотеку C++11
- Онлайн-компилятор C++11
- Бьярна Страуструпа Часто задаваемые вопросы по C++11
- Дополнительная информация о функциях C++11: цикл for на основе диапазона, почему auto_ptr устарел и т. д.