C++20 включает в себя следующие новые возможности языка:
C++20 включает в себя следующие новые возможности библиотеки:
C++17 включает следующие новые возможности языка:
C++17 включает следующие новые возможности библиотеки:
C++14 включает в себя следующие новые возможности языка:
C++14 включает следующие новые возможности библиотеки:
C++11 включает в себя следующие новые возможности языка:
C++11 включает следующие новые возможности библиотеки:
Примечание. Хотя эти примеры иллюстрируют, как использовать сопрограммы на базовом уровне, при компиляции кода происходит гораздо больше. Эти примеры не предназначены для полного описания сопрограмм C++20. Поскольку классы
generator
иtask
пока не предусмотрены стандартной библиотекой, для компиляции этих примеров я использовал библиотеку cppcoro.
Сопрограммы — это специальные функции, выполнение которых можно приостанавливать и возобновлять. Чтобы определить сопрограмму, в теле функции должны присутствовать ключевые слова co_return
, co_await
или co_yield
. Сопрограммы C++20 не имеют стека; если их состояние не оптимизировано компилятором, они размещаются в куче.
Примером сопрограммы является функция- генератор , которая выдает (т.е. генерирует) значение при каждом вызове:
generator< int > range ( int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for ( int n : range( 0 , 10 )) {
std::cout << n << std::endl;
}
Вышеупомянутая функция генератора range
генерирует значения, начиная с start
и заканчивая end
(исключительно), причем каждый шаг итерации дает текущее значение, хранящееся в start
. Генератор сохраняет свое состояние при каждом вызове range
(в данном случае вызов выполняется для каждой итерации цикла for). co_yield
принимает заданное выражение, выдает (то есть возвращает) его значение и приостанавливает сопрограмму в этой точке. При возобновлении выполнение продолжается после co_yield
.
Другим примером сопрограммы является задача , которая представляет собой асинхронное вычисление, которое выполняется, когда ожидается выполнение задачи:
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
В этом примере введено ключевое слово co_await
. Это ключевое слово принимает выражение и приостанавливает выполнение, если то, что вы ожидаете (в данном случае чтение или запись), не готово, в противном случае вы продолжаете выполнение. (Обратите внимание, что на самом деле co_yield
использует co_await
.)
Использование задачи для ленивой оценки значения:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Концепции называются предикатами времени компиляции, которые ограничивают типы. Они принимают следующую форму:
template < template-parameter-list >
concept concept-name = constraint-expression;
где constraint-expression
оценивается как логическое значение constexpr. Ограничения должны моделировать семантические требования, например, является ли тип числовым или хешируемым. Ошибка компилятора возникает, если данный тип не удовлетворяет концепции, с которой он связан (т. е. constraint-expression
возвращает false
). Поскольку ограничения оцениваются во время компиляции, они могут предоставлять более содержательные сообщения об ошибках и безопасность во время выполнения.
// `T` is not limited by any constraints.
template < typename T>
concept always_satisfied = true ;
// Limit `T` to integrals.
template < typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template < typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template < typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
Существует множество синтаксических форм для реализации концепций:
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
requires my_concept<T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
void f (T v) requires my_concept<T>;
// `v` is a constrained deduced parameter.
void f (my_concept auto v);
// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g ();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> requires my_concept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> (T v) requires my_concept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
// ...
};
Ключевое слово requires
используется либо для начала предложения requires
, либо для начала выражения requires
:
template < typename T>
requires my_concept<T> // `requires` clause.
void f (T);
template < typename T>
concept callable = requires (T f) { f (); }; // `requires` expression.
template < typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add (T a, T b) {
return a + b;
}
Обратите внимание, что список параметров в выражении requires
не является обязательным. Каждое требование в выражении requires
является одним из следующих:
template < typename T>
concept callable = requires (T f) { f (); };
typename
за которым следует имя типа, и утверждают, что данное имя типа допустимо. struct foo {
int foo;
};
struct bar {
using value = int ;
value data;
};
struct baz {
using value = int ;
value data;
};
// Using SFINAE, enable if `T` is a `baz`.
template < typename T, typename = std:: enable_if_t <std::is_same_v<T, baz>>>
struct S {};
template < typename T>
using Ref = T&;
template < typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g (T a);
g (foo{}); // ERROR: Fails requirement A.
g (bar{}); // ERROR: Fails requirement B.
g (baz{}); // PASS.
template < typename T>
concept C = requires(T x) {
{*x} -> std::convertible_to< typename T::inner>; // the type of the expression `*x` is convertible to `T::inner`
{x + 1 } -> std::same_as< int >; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
{x * 1 } -> std::convertible_to<T>; // the type of the expression `x * 1` is convertible to `T`
};
requires
, указывают дополнительные ограничения (например, ограничения на аргументы локальных параметров). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
См. также: библиотека концепций.
В C++20 представлен оператор космического корабля ( <=>
) как новый способ написания функций сравнения, который сокращает шаблонность и помогает разработчикам определить более четкую семантику сравнения. Определение оператора трехстороннего сравнения автоматически сгенерирует другие функции оператора сравнения (т. е. ==
, !=
, <
и т. д.).
Введены три порядка:
std::strong_ordering
: строгий порядок различает элементы, которые являются равными (идентичными и взаимозаменяемыми). Обеспечивает порядок less
, greater
, equivalent
и equal
. Примеры сравнений: поиск определенного значения в списке, значения целых чисел, строки с учетом регистра.std::weak_ordering
: слабый порядок различает эквивалентные элементы (не идентичные, но могут быть взаимозаменяемыми для целей сравнения). Обеспечивает less
, greater
и equivalent
порядок. Примеры сравнений: строки без учета регистра, сортировка, сравнение некоторых, но не всех видимых членов класса.std::partial_ordering
: Частичное упорядочение следует тому же принципу слабого упорядочения, но включает случай, когда упорядочение невозможно. Обеспечивает less
, greater
, equivalent
и unordered
порядок. Примеры сравнений: значения с плавающей запятой (например, NaN
).Оператор трехстороннего сравнения по умолчанию выполняет почленное сравнение:
struct foo {
int a;
bool b;
char c;
// Compare `a` first, then `b`, then `c` ...
auto operator <=>( const foo&) const = default ;
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == true
f1 == f2; // == false
f1 >= f2; // == false
Вы также можете определить свои собственные сравнения:
struct foo {
int x;
bool b;
char c;
std::strong_ordering operator <=>( const foo& other) const {
return x <=> other. x ;
}
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == false
f1 == f2; // == true
f1 >= f2; // == true
Синтаксис инициализатора в стиле C. Любые поля-члены, которые не указаны явно в назначенном списке инициализаторов, инициализируются по умолчанию.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Используйте знакомый синтаксис шаблонов в лямбда-выражениях.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Эта функция упрощает общие шаблоны кода, помогает ограничить области действия и предлагает элегантное решение распространенной проблемы, возникающей в течение всего срока службы.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Предоставляет подсказку оптимизатору о том, что помеченный оператор имеет высокую вероятность выполнения.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Если один из атрибутов «вероятно/маловероятно» появляется после правой круглой скобки оператора if, это указывает на то, что ветка, скорее всего/маловероятно, выполнит свой подоператор (тело).
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
Его также можно применить к подвыражению (телу) оператора итерации.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
Неявное фиксирование this
при лямбда-захвате с использованием [=]
теперь устарело; предпочитайте явный захват с использованием [=, this]
или [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
Классы теперь можно использовать в параметрах шаблона, не являющихся типами. Объекты, передаваемые в качестве аргументов шаблона, имеют тип const T
, где T
— тип объекта, и имеют статическую продолжительность хранения.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
Виртуальные функции теперь могут быть constexpr
и оценены во время компиляции. Виртуальные функции constexpr
могут переопределять виртуальные функции, отличные от constexpr
, и наоборот.
struct X1 {
virtual int f () const = 0;
};
struct X2 : public X1 {
constexpr virtual int f () const { return 2 ; }
};
struct X3 : public X2 {
virtual int f () const { return 3 ; }
};
struct X4 : public X3 {
constexpr virtual int f () const { return 4 ; }
};
constexpr X4 x4;
x4.f(); // == 4
Условно выберите во время компиляции, является ли конструктор явным или нет. explicit(true)
— то же самое, что указание explicit
.
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template < typename T>
explicit (!std::is_integral_v<T>) foo(T) {}
};
foo a = 123 ; // OK
foo b = " 123 " ; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c { " 123 " }; // OK
Аналогично функциям constexpr
, но функции со спецификатором consteval
должны создавать константу. Они называются immediate functions
.
consteval int sqr ( int n) {
return n * n;
}
constexpr int r = sqr( 100 ); // OK
int x = 100 ;
int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression
// OK if `sqr` were a `constexpr` function
Добавьте члены перечисления в область видимости, чтобы улучшить читаемость. До:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return " red " ;
case rgba_color_channel::green: return " green " ;
case rgba_color_channel::blue: return " blue " ;
case rgba_color_channel::alpha: return " alpha " ;
}
}
После:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return " red " ;
case green: return " green " ;
case blue: return " blue " ;
case alpha: return " alpha " ;
}
}
Захват пакетов параметров по значению:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Захват пакетов параметров по ссылке:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Предоставляет стандартный тип для представления строк UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
Спецификатор constinit
требует, чтобы переменная была инициализирована во время компиляции.
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
Помогает поддерживать макросы с вариациями, оценивая заданный аргумент, если макрос с вариациями не пуст.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
Концепции также предоставляются стандартной библиотекой для создания более сложных концепций. Некоторые из них включают в себя:
Основные концепции языка:
same_as
— указывает, что два типа одинаковы.derived_from
— указывает, что тип является производным от другого типа.convertible_to
— указывает, что тип неявно преобразуется в другой тип.common_with
— указывает, что два типа имеют общий тип.integral
— указывает, что тип является целочисленным.default_constructible
— указывает, что объект типа может быть создан по умолчанию.Концепции сравнения:
boolean
— указывает, что тип может использоваться в логических контекстах.equality_comparable
— указывает, что operator==
является отношением эквивалентности.Концепции объекта:
movable
— указывает, что объект определенного типа можно перемещать и менять местами.copyable
— указывает, что объект определенного типа можно копировать, перемещать и заменять местами.semiregular
— указывает, что объект определенного типа можно копировать, перемещать, заменять местами и создавать по умолчанию.regular
— указывает, что тип является регулярным , то есть он является одновременно semiregular
и equality_comparable
.Вызываемые концепции:
invocable
— указывает, что вызываемый тип может быть вызван с заданным набором типов аргументов.predicate
— указывает, что вызываемый тип является логическим предикатом.См. также: понятия.
Объедините простоту printf
с безопасностью типов iostream
. Использует фигурные скобки в качестве заполнителей и поддерживает пользовательское форматирование, аналогичное спецификаторам в стиле printf.
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
Чтобы отформатировать пользовательские типы:
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
Буферизирует операции вывода для обернутого выходного потока, обеспечивая синхронизацию (т. е. отсутствие чередования вывода).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Промежуток — это представление (т. е. отсутствие владения) контейнера, обеспечивающее доступ с проверкой границ к непрерывной группе элементов. Поскольку представления не владеют своими элементами, их легко создавать и копировать. Проще говоря, представления содержат ссылки на свои данные. В отличие от поддержки указателя/итератора и поля длины, диапазон объединяет их в один объект.
Промежутки могут иметь динамический или фиксированный размер (известный как их экстент ). Промежутки фиксированного размера выигрывают от проверки границ.
Span не распространяет const, поэтому для создания диапазона, доступного только для чтения, используйте std::span<const T>
.
Пример: использование диапазона динамического размера для печати целых чисел из различных контейнеров.
void print_ints (std::span< const int > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_ints (std::vector{ 1 , 2 , 3 });
print_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 });
int a[ 10 ] = { 0 };
print_ints (a);
// etc.
Пример: диапазон статического размера не сможет скомпилироваться для контейнеров, размер которых не соответствует размеру диапазона.
void print_three_ints (std::span< const int , 3 > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_three_ints (std::vector{ 1 , 2 , 3 }); // ERROR
print_three_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 }); // ERROR
int a[ 10 ] = { 0 };
print_three_ints (a); // ERROR
std::array< int , 3 > b = { 1 , 2 , 3 };
print_three_ints (b); // OK
// You can construct a span manually if required:
std::vector c{ 1 , 2 , 3 };
print_three_ints (std::span< const int , 3 >{ c. data (), 3 }); // OK: set pointer and length field.
print_three_ints (std::span< const int , 3 >{ c. cbegin (), c. cend () }); // OK: use iterator pairs.
В C++20 предусмотрен новый заголовок <bit>
, который обеспечивает некоторые битовые операции, включая popcount.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Математические константы, включая PI, число Эйлера и т. д., определенные в заголовке <numbers>
.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Функция-предикат, которая является истинной, когда она вызывается в контексте времени компиляции.
constexpr bool is_compile_time () {
return std::is_constant_evaluated ();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
auto p = std::make_shared< int []>( 5 ); // pointer to `int[5]`
// OR
auto p = std::make_shared< int [ 5 ]>(); // pointer to `int[5]`
Строки (и представления строк) теперь имеют функции-члены starts_with
и ends_with
позволяющие проверить, начинается или заканчивается строка данной строкой.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Ассоциативные контейнеры, такие как наборы и карты, имеют функцию-член contains
, которую можно использовать вместо идиомы «найти и проверить конец итератора».
std::map< int , char > map {{ 1 , ' a ' }, { 2 , ' b ' }};
map.contains( 2 ); // true
map.contains( 123 ); // false
std::set< int > set { 1 , 2 , 3 };
set.contains( 2 ); // true
Более безопасный способ интерпретировать объект из одного типа в другой.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Безопасно вычислить среднюю точку двух целых чисел (без переполнения).
std::midpoint ( 1 , 3 ); // == 2
Преобразует данный массив/объект, подобный массиву, в std::array
.
std::to_array ( " foo " ); // returns `std::array<char, 4>`
std::to_array< int >({ 1 , 2 , 3 }); // returns `std::array<int, 3>`
int a[] = { 1 , 2 , 3 };
std::to_array (a); // returns `std::array<int, 3>`
Связывает первые N аргументов (где N — количество аргументов после заданной функции для std::bind_front
) с заданной свободной функцией, лямбда-выражением или функцией-членом.
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
Предоставляет std::erase
и/или std::erase_if
для различных контейнеров STL, таких как строка, список, вектор, карта и т. д.
Для стирания по значению используйте std::erase
или для указания предиката, когда стирать элементы, используйте std::erase_if
. Обе функции возвращают количество стертых элементов.
std::vector v{ 0 , 1 , 0 , 2 , 0 , 3 };
std::erase (v, 0 ); // v == {1, 2, 3}
std::erase_if (v, []( int n) { return n == 0 ; }); // v == {1, 2, 3}
Вспомогательные функции для присвоения имен результатам сравнения:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
См. также: трехстороннее сравнение.
Лексикографически сравнивает два диапазона, используя трехстороннее сравнение, и выдает результат наиболее сильного применимого типа категории сравнения.
std::vector a{ 0 , 0 , 0 }, b{ 0 , 0 , 0 }, c{ 1 , 1 , 1 };
auto cmp_ab = std::lexicographical_compare_three_way(
a.begin(), a.end(), b.begin(), b.end());
std::is_eq (cmp_ab); // == true
auto cmp_ac = std::lexicographical_compare_three_way(
a.begin(), a.end(), c.begin(), c.end());
std::is_lt (cmp_ac); // == true
См. также: трехстороннее сравнение, помощники трехстороннего сравнения.
Автоматический вывод аргументов шаблона очень похож на то, как это делается для функций, но теперь включает конструкторы классов.
template < typename T = float >
struct MyContainer {
T val;
MyContainer () : val{} {}
MyContainer (T val) : val{val} {}
// ...
};
MyContainer c1 { 1 }; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>
Следуя правилам вывода auto
, при соблюдении списка допустимых типов нетиповых параметров шаблона[*], аргументы шаблона могут быть выведены из типов его аргументов:
template < auto ... seq>
struct my_integer_sequence {
// Implementation here ...
};
// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence< int , 0 , 1 , 2 >();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence< 0 , 1 , 2 >();
* — Например, вы не можете использовать double
в качестве типа параметра шаблона, что также делает недопустимым вывод с использованием auto
.
Выражение свертывания выполняет свертывание пакета параметров шаблона с помощью бинарного оператора.
(... op e)
или (e op ...)
, где op
— оператор свертки, а e
— нерасширенный пакет параметров, называются унарными свертками .(e1 op ... op e2)
, где op
— операторы свертки, называется бинарной сверткой . Либо e1
, либо e2
— это нерасширенный пакет параметров, но не оба одновременно. template < typename ... Args>
bool logicalAnd (Args... args) {
// Binary folding.
return ( true && ... && args);
}
bool b = true ;
bool & b2 = b;
logicalAnd (b, b2, true ); // == true
template < typename ... Args>
auto sum (Args... args) {
// Unary folding.
return (... + args);
}
sum ( 1.0 , 2 . 0f , 3 ); // == 6.0
Изменения в auto
выводе при использовании с унифицированным синтаксисом инициализации. Ранее auto x {3};
выводит std::initializer_list<int>
, который теперь выводит к int
.
auto x1 { 1 , 2 , 3 }; // error: not a single element
auto x2 = { 1 , 2 , 3 }; // x2 is std::initializer_list<int>
auto x3 { 3 }; // x3 is int
auto x4 { 3.0 }; // x4 is double
Лямбды времени компиляции с использованием constexpr
.
auto identity = []( int n) constexpr { return n; };
static_assert (identity( 123 ) == 123);
constexpr auto add = []( int x, int y) {
auto L = [=] { return x; };
auto R = [=] { return y; };
return [=] { return L () + R (); };
};
static_assert (add( 1 , 2 )() == 3);
constexpr int addOne ( int n) {
return [n] { return n + 1 ; }();
}
static_assert (addOne( 1 ) == 2);
this
по значению Захват this
в среде лямбды ранее был только справочным. Примером того, где это проблематично, является асинхронный код, использующий обратные вызовы, которые требуют, чтобы объект был доступен после потенциально истечения срока его существования. *this
(C++17) теперь создаст копию текущего объекта, а this
(C++11) продолжит захват по ссылке.
struct MyObj {
int value { 123 };
auto getValueCopy () {
return [* this ] { return value; };
}
auto getValueRef () {
return [ this ] { return value; };
}
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321 ;
valueCopy (); // 123
valueRef (); // 321
Спецификатор inline можно применять как к переменным, так и к функциям. Переменная, объявленная как встроенная, имеет ту же семантику, что и функция, объявленная как встроенная.
// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{ 321 }; // mov esi, dword ptr [x1]
// x1: .long 321
S x2 = S{ 123 }; // mov eax, dword ptr [.L_ZZ4mainE2x2]
// mov dword ptr [rbp - 8], eax
// .L_ZZ4mainE2x2: .long 123
Его также можно использовать для объявления и определения статической переменной-члена, поэтому ее не нужно инициализировать в исходном файле.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Использование оператора разрешения пространства имен для создания вложенных определений пространства имен.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
Код выше можно написать так:
namespace A ::B::C {
int i;
}
Предложение по деструктуризации инициализации, которое позволило бы написать auto [ x, y, z ] = expr;
где типом expr
был объект, похожий на кортеж, элементы которого были бы привязаны к переменным x
, y
и z
(которые объявляются в этой конструкции). К объектам, подобным кортежам, относятся std::tuple
, std::pair
, std::array
и агрегатные структуры.
using Coordinate = std::pair< int , int >;
Coordinate origin () {
return Coordinate{ 0 , 0 };
}
const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map<std::string, int > mapping {
{ " a " , 1 },
{ " b " , 2 },
{ " c " , 3 }
};
// Destructure by reference.
for ( const auto & [key, value] : mapping) {
// Do something with key and value
}
Новые версии операторов if
и switch
, которые упрощают общие шаблоны кода и помогают пользователям соблюдать ограничения.
{
std::lock_guard<std::mutex> lk (mx);
if (v. empty ()) v. push_back (val);
}
// vs.
if (std::lock_guard<std::mutex> lk (mx); v.empty()) {
v. push_back (val);
}
Foo gadget (args);
switch ( auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
// vs.
switch (Foo gadget (args); auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
Напишите код, экземпляр которого создается в зависимости от условия времени компиляции.
template < typename T>
constexpr bool isIntegral () {
if constexpr (std::is_integral<T>::value) {
return true ;
} else {
return false ;
}
}
static_assert (isIntegral< int >() == true);
static_assert (isIntegral< char >() == true);
static_assert (isIntegral< double >() == false);
struct S {};
static_assert (isIntegral<S>() == false);
Символьный литерал, начинающийся с u8
является символьным литералом типа char
. Значение символьного литерала UTF-8 равно значению его кодовой точки ISO 10646.
char x = u8 ' x ' ;
Перечисления теперь можно инициализировать с использованием синтаксиса в фигурных скобках.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
В C++17 представлены три новых атрибута: [[fallthrough]]
, [[nodiscard]]
и [[maybe_unused]]
.
[[fallthrough]]
указывает компилятору, что провал в операторе переключения является предполагаемым поведением. Этот атрибут можно использовать только в операторе переключения и он должен быть помещен перед следующей меткой случая/по умолчанию. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
выдает предупреждение, когда функция или класс имеет этот атрибут и ее возвращаемое значение отбрасывается. [[nodiscard]] bool do_something () {
return is_success; // true for success, false for failure
}
do_something (); // warning: ignoring return value of 'bool do_something()',
// declared with attribute 'nodiscard'
// Only issues a warning when `error_info` is returned by value.
struct [[nodiscard]] error_info {
// ...
};
error_info do_something () {
error_info ei;
// ...
return ei;
}
do_something (); // warning: ignoring returned value of type 'error_info',
// declared with attribute 'nodiscard'
[[maybe_unused]]
указывает компилятору, что переменная или параметр могут быть неиспользуемыми и предназначены для них. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
Оператор __has_include (operand)
можно использовать в выражениях #if
и #elif
чтобы проверить, доступен ли заголовок или исходный файл ( operand
) для включения или нет.
Одним из вариантов использования этого может быть использование двух библиотек, которые работают одинаково, используя резервную/экспериментальную, если предпочтительная не найдена в системе.
# ifdef __has_include
# if __has_include(<optional>)
# include < optional >
# define have_optional 1
# elif __has_include(<experimental/optional>)
# include < experimental/optional >
# define have_optional 1
# define experimental_optional
# else
# define have_optional 0
# endif
# endif
Его также можно использовать для включения заголовков, существующих под разными именами или в разных местах на разных платформах, не зная, на какой платформе работает программа. Хорошим примером являются заголовки OpenGL, которые расположены в каталоге OpenGL
на macOS и GL
на других. платформы.
# ifdef __has_include
# if __has_include(<OpenGL/gl.h>)
# include < OpenGL/gl.h >
# include < OpenGL/glu.h >
# elif __has_include(<GL/gl.h>)
# include < GL/gl.h >
# include < GL/glu.h >
# else
# error No suitable OpenGL headers found.
# endif
# endif
Вывод аргументов шаблона класса (CTAD) позволяет компилятору выводить аргументы шаблона из аргументов конструктора.
std::vector v{ 1 , 2 , 3 }; // deduces std::vector<int>
std::mutex mtx;
auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard<std::mutex>
auto p = new std::pair{ 1.0 , 2.0 }; // deduces to std::pair<double, double>*
Для пользовательских типов можно использовать руководства по выводу, которые помогут компилятору вывести аргументы шаблона, если это применимо:
template < typename T>
struct container {
container (T t) {}
template < typename Iter>
container (Iter beg, Iter end);
};
// deduction guide
template < typename Iter>
container (Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
container a{ 7 }; // OK: deduces container<int>
std::vector< double > v{ 1.0 , 2.0 , 3.0 };
auto b = container{ v. begin (), v. end () }; // OK: deduces container<double>
container c{ 5 , 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
Шаблон класса std::variant
представляет типобезопасное union
. Экземпляр std::variant
в любой момент времени содержит значение одного из альтернативных типов (также возможно, что он не имеет значения).
std::variant< int , double > v{ 12 };
std::get< int >(v); // == 12
std::get< 0 >(v); // == 12
v = 12.0 ;
std::get< double >(v); // == 12.0
std::get< 1 >(v); // == 12.0
Шаблон класса std::optional
управляет необязательным содержащимся значением, то есть значением, которое может присутствовать или отсутствовать. Распространенным вариантом использования необязательного параметра является возвращаемое значение функции, которая может завершиться ошибкой.
std::optional<std::string> create ( bool b) {
if (b) {
return " Godzilla " ;
} else {
return {};
}
}
create ( false ).value_or( " empty " ); // == "empty"
create ( true ).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if ( auto str = create( true )) {
// ...
}
Типобезопасный контейнер для отдельных значений любого типа.
std::any x { 5 };
x.has_value() // == true
std::any_cast< int >(x) // == 5
std::any_cast< int &>(x) = 10 ;
std::any_cast< int >(x) // == 10
Ссылка, не владеющая строкой. Полезно для создания абстракции поверх строк (например, для анализа).
// Regular strings.
std::string_view cppstr { " foo " };
// Wide strings.
std::wstring_view wcstr_v { L" baz " };
// Character arrays.
char array[ 3 ] = { ' b ' , ' a ' , ' r ' };
std::string_view array_v (array, std::size(array));
std::string str { " trim me " };
std::string_view v {str};
v.remove_prefix(std::min(v.find_first_not_of( " " ), v.size()));
str; // == " trim me"
v; // == "trim me"
Вызов объекта Callable
с параметрами. Примерами вызываемых объектов являются std::function
или лямбда-выражения; объекты, которые можно вызывать аналогично обычной функции.
template < typename Callable>
class Proxy {
Callable c_;
public:
Proxy (Callable c) : c_{ std::move (c) } {}
template < typename ... Args>
decltype ( auto ) operator ()(Args&&... args) {
// ...
return std::invoke (c_, std::forward<Args>(args)...);
}
};
const auto add = []( int x, int y) { return x + y; };
Proxy p{ add };
p ( 1 , 2 ); // == 3
Вызов объекта Callable
с кортежем аргументов.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
Новая библиотека std::filesystem
предоставляет стандартный способ управления файлами, каталогами и путями в файловой системе.
Здесь большой файл копируется во временный путь, если есть свободное место:
const auto bigFilePath { " bigFileToCopy " };
if (std::filesystem::exists(bigFilePath)) {
const auto bigFileSize { std::filesystem::file_size (bigFilePath)};
std::filesystem::path tmpPath { " /tmp " };
if ( std::filesystem::space (tmpPath). available > bigFileSize) {
std::filesystem::create_directory (tmpPath. append ( " example " ));
std::filesystem::copy_file (bigFilePath, tmpPath. append ( " newFile " ));
}
}
Новый тип std::byte
обеспечивает стандартный способ представления данных в виде байтов. Преимущества использования std::byte
вместо char
или unsigned char
заключаются в том, что это не символьный тип, а также не арифметический тип; в то время как единственные доступные перегрузки операторов — это побитовые операции.
std::byte a { 0 };
std::byte b { 0xFF };
int i = std::to_integer< int >(b); // 0xFF
std::byte c = a & b;
int j = std::to_integer< int >(c); // 0
Обратите внимание, что std::byte
— это просто перечисление, и инициализация перечислений в фигурных скобках становится возможной благодаря инициализации перечислений прямым списком.
Перемещение узлов и объединение контейнеров без затрат на дорогостоящее копирование, перемещение или выделение/освобождение кучи.
Перемещение элементов с одной карты на другую:
std::map< int , string> src {{ 1 , " one " }, { 2 , " two " }, { 3 , " buckle my shoe " }};
std::map< int , string> dst {{ 3 , " three " }};
dst.insert(src.extract(src.find( 1 ))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract( 2 )); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };
Вставляем весь набор:
std::set< int > src { 1 , 3 , 5 };
std::set< int > dst { 2 , 4 , 5 };
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }
Вставка элементов, которые переживут контейнер:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Изменение ключа элемента карты:
std::map< int , string> m {{ 1 , " one " }, { 2 , " two " }, { 3 , " three " }};
auto e = m.extract( 2 );
e.key() = 4 ;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }
Многие алгоритмы STL, такие как методы copy
, find
и sort
, начали поддерживать политики параллельного выполнения : seq
, par
и par_unseq
которые переводятся как «последовательно», «параллельно» и «параллельно без последовательности».
std::vector< int > longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2 );
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));
Выбирает n элементов в заданной последовательности (без замены), где каждый элемент имеет равные шансы быть выбранным.
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
Зафиксируйте данное значение между нижней и верхней границей.
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
Сложите заданный диапазон элементов. Концептуально похоже на std::accumulate
, но std::reduce
будет выполнять свертывание параллельно. Поскольку свертывание выполняется параллельно, если вы указываете бинарную операцию, она должна быть ассоциативной и коммутативной. Данная бинарная операция также не должна изменять какой-либо элемент или делать недействительными итераторы в заданном диапазоне.
Бинарная операция по умолчанию — std::plus с начальным значением 0.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
Дополнительно вы можете указать преобразования для редьюсеров:
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
Поддержка сумм префиксов (как инклюзивных, так и исключительных сканирований) вместе с преобразованиями.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
Наибольший общий делитель (НОД) и наименьшее общее кратное (НОК).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Служебная функция, возвращающая отрицание результата данной функции.
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
Преобразуйте целые числа и числа с плавающей запятой в строку и наоборот. Преобразования не выбрасывают, не выделяют и более безопасны, чем эквиваленты из стандартной библиотеки C.
Пользователи несут ответственность за выделение достаточного объема памяти, необходимого для std::to_chars
, иначе функция завершится ошибкой из-за установки объекта кода ошибки в ее возвращаемом значении.
Эти функции позволяют вам дополнительно передавать базу (по умолчанию — 10) или спецификатор формата для ввода с плавающим типом.
std::to_chars
возвращает (неконстантный) указатель на символ, который находится на один конец строки, в которую функция записала внутри данного буфера, и объект кода ошибки.std::from_chars
возвращает указатель const char, который в случае успеха равен конечному указателю, переданному функции, и объект кода ошибки.Оба объекта кода ошибки, возвращаемые этими функциями, в случае успеха равны объекту кода ошибки, инициализированному по умолчанию.
Преобразуйте число 123
в std::string
:
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
Преобразование из std::string
со значением "123"
в целое число:
const std::string str{ " 123 " };
int n;
const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << n << std::endl; } // 123
else { /* handle failure */ }
Предоставляет вспомогательные функции abs, round, ceil и Floor для std::chrono::duration
и std::chrono::time_point
.
using seconds = std::chrono::seconds;
std::chrono::milliseconds d{ 5500 };
std::chrono::abs (d); // == 5s
std::chrono::round<seconds>(d); // == 6s
std::chrono::ceil<seconds>(d); // == 6s
std::chrono::floor<seconds>(d); // == 5s
Двоичные литералы предоставляют удобный способ представления чисел с основанием 2. Разделить цифры можно с помощью '
.
0b110 // == 6
0b1111'1111 // == 255
C++14 теперь допускает auto
спецификатор типа в списке параметров, что позволяет использовать полиморфные лямбда-выражения.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Это позволяет создавать лямбда-захваты, инициализированные произвольными выражениями. Имя, присвоенное захваченному значению, не обязательно должно быть связано с какими-либо переменными во вмещающих областях и вводит новое имя внутри тела лямбда-выражения. Выражение инициализации оценивается при создании лямбды (а не при ее вызове ).
int factory ( int i) { return i * 10 ; }
auto f = [x = factory( 2 )] { return x; }; // returns 20
auto generator = [x = 0 ] () mutable {
// this would not compile without 'mutable' as we are modifying x on each call
return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2
Поскольку теперь можно перемещать (или пересылать ) значения в лямбду, которые раньше можно было захватить только путем копирования или ссылки, теперь мы можем захватывать типы, доступные только для перемещения, в лямбде по значению. Обратите внимание, что в приведенном ниже примере p
в списке захвата task2
слева от =
является новой переменной, частной для тела лямбда-выражения и не ссылается на исходный p
.
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5 ; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created
Использование этого захвата ссылок может иметь имена, отличные от имен ссылочной переменной.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
Используя тип auto
возврата в C++14, компилятор попытается определить тип за вас. С помощью лямбда-выражений теперь вы можете определить тип возвращаемого значения с помощью auto
, что делает возможным возврат выведенной ссылки или ссылки rvalue.
// Deduce return type as `int`.
auto f ( int i) {
return i;
}
template < typename T>
auto & f (T& t) {
return t;
}
// Returns a reference to a deduced type.
auto g = []( auto & x) -> auto & { return f (x); };
int y = 123 ;
int & z = g(y); // reference to `y`
Спецификатор типа decltype(auto)
также выводит тип, как это делает auto
. Однако он выводит возвращаемые типы, сохраняя при этом их ссылки и cv-квалификаторы, а auto
— нет.
const int x = 0 ;
auto x1 = x; // int
decltype ( auto ) x2 = x; // const int
int y = 0 ;
int & y1 = y;
auto y2 = y1; // int
decltype ( auto ) y3 = y1; // int&
int && z = 0 ;
auto z1 = std::move(z); // int
decltype ( auto ) z2 = std::move(z); // int&&
// Note: Especially useful for generic code!
// Return type is `int`.
auto f ( const int & i) {
return i;
}
// Return type is `const int&`.
decltype ( auto ) g( const int & i) {
return i;
}
int x = 123 ;
static_assert (std::is_same< const int &, decltype(f(x))>::value == 0);
static_assert (std::is_same< int , decltype(f(x))>::value == 1);
static_assert (std::is_same< const int &, decltype(g(x))>::value == 1);
См. также: decltype (C++11)
.
В C++11 тела функций constexpr
могли содержать только очень ограниченный набор синтаксисов, включая (но не ограничиваясь): typedef
s, using
s и один оператор return
. В C++14 набор допустимых синтаксисов значительно расширяется и включает в себя наиболее распространенный синтаксис, такой как операторы if
, множественный return
, циклы и т. д.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14 позволяет создавать шаблоны переменных:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
В C++14 введен атрибут [[deprecated]]
для обозначения того, что модуль (функция, класс и т. д.) не рекомендуется и может привести к появлению предупреждений компиляции. Если указана причина, она будет включена в предупреждения.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Новые пользовательские литералы для типов стандартных библиотек, включая новые встроенные литералы для chrono
и basic_string
. Это могут быть constexpr
, что означает, что их можно использовать во время компиляции. Некоторые варианты использования этих литералов включают анализ целых чисел во время компиляции, двоичные литералы и литералы мнимых чисел.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
Шаблон класса std::integer_sequence
представляет собой последовательность целых чисел времени компиляции. Есть несколько помощников, встроенных сверху:
std::make_integer_sequence<T, N>
— создает последовательность 0, ..., N - 1
с типом T
std::index_sequence_for<T...>
— преобразует пакет параметров шаблона в целочисленную последовательность.Преобразуйте массив в кортеж:
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
— рекомендуемый способ создания экземпляров std::unique_ptr
по следующим причинам:
new
оператора.foo
следующим образом: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
Компилятор может вызвать new T{}
, затем function_that_throws()
и так далее... Поскольку мы разместили данные в куче в первой конструкции T
, мы ввели здесь утечку. С помощью std::make_unique
нам предоставляется безопасность исключений:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Дополнительную информацию о std::unique_ptr
и std::shared_ptr
. в разделе «умные указатели» (C++11).
Перемещение объекта означает передачу владения некоторым ресурсом, которым он управляет, другому объекту.
Первым преимуществом семантики перемещения является оптимизация производительности. Когда объект приближается к концу своего существования, либо потому, что он является временным, либо из-за явного вызова std::move
, перемещение часто является более дешевым способом передачи ресурсов. Например, перемещение std::vector
— это просто копирование некоторых указателей и внутреннего состояния в новый вектор. Копирование потребует копирования каждого отдельного содержащегося элемента в векторе, что является дорогостоящим и ненужным, если старый вектор скоро будет уничтожен. разрушен.
Движения также позволяют некопируемым типам, таким как std::unique_ptr
(умные указатели), гарантировать на уровне языка, что одновременно существует только один экземпляр управляемого ресурса, при этом имея возможность передавать экземпляр. между областями.
См. разделы: ссылки rvalue, специальные функции-члены для семантики перемещения, std::move
, std::forward
, forwarding references
.
В C++11 представлена новая ссылка, называемая ссылкой rvalue . Ссылка rvalue на T
, который является параметром нешаблонного типа (например, int
или определяемого пользователем типа), создается с помощью синтаксиса T&&
. Ссылки Rvalue привязываются только к rvalue.
Введите вывод с помощью lvalues и rvalues:
int x = 0 ; // `x` is an lvalue of type `int`
int & xl = x; // `xl` is an lvalue of type `int&`
int && xr = x; // compiler error -- `x` is an lvalue
int && xr2 = 0 ; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
void f ( int & x) {}
void f ( int && x) {}
f (x); // calls f(int&)
f (xl); // calls f(int&)
f ( 3 ); // calls f(int&&)
f (std::move(x)); // calls f(int&&)
f (xr2); // calls f(int&)
f (std::move(xr2)); // calls f(int&& x)
См. также: std::move
, std::forward
, forwarding references
.
Также известен (неофициально) как универсальные ссылки . Ссылка на пересылку создается с помощью синтаксиса T&&
где T
— параметр типа шаблона, или с использованием auto&&
. Это обеспечивает идеальную пересылку : возможность передавать аргументы, сохраняя при этом их категорию значений (например, lvalue остаются как lvalue, временные значения пересылаются как rvalue).
Ссылки пересылки позволяют ссылке привязываться либо к lvalue, либо к rvalue в зависимости от типа. Пересылка ссылок подчиняется правилам свертывания ссылок :
T& &
становится T&
T& &&
становится T&
T&& &
становится T&
T&& &&
становится T&&
auto
вывод типов с помощью lvalue и rvalue:
int x = 0 ; // `x` is an lvalue of type `int`
auto && al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto && ar = 0 ; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
Вывод параметров типа шаблона с помощью lvalue и rvalue:
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
См. также: std::move
, std::forward
, rvalue references
.
Синтаксис ...
создает пакет параметров или расширяет его. Пакет параметров шаблона — это параметр шаблона, который принимает ноль или более аргументов шаблона (не типов, типов или шаблонов). Шаблон хотя бы с одним пакетом параметров называется вариативным шаблоном .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Интересное использование этого — создание списка инициализаторов из пакета параметров для перебора аргументов функции с переменным числом аргументов.
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
Облегченный контейнер элементов, похожий на массив, созданный с использованием синтаксиса «списка в фигурных скобках». Например, { 1, 2, 3 }
создает последовательность целых чисел типа std::initializer_list<int>
. Полезно в качестве замены передачи вектора объектов в функцию.
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
Утверждения, которые оцениваются во время компиляции.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
auto
типизированные переменные выводятся компилятором в соответствии с типом их инициализатора.
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
Чрезвычайно полезно для удобства чтения, особенно для сложных типов:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
Функции также могут определить тип возвращаемого значения, используя auto
. В C++11 тип возвращаемого значения должен быть указан либо явно, либо с использованием decltype
, например:
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
Завершающий тип возвращаемого значения в приведенном выше примере — это объявленный тип (см. раздел decltype
) выражения x + y
. Например, если x
— целое число, а y
— двойное число, decltype(x + y)
— двойное. Следовательно, приведенная выше функция определит тип в зависимости от того, какой тип дает выражение x + y
. Обратите внимание, что конечный возвращаемый тип имеет доступ к своим параметрам, и this
при необходимости.
lambda
— это безымянный функциональный объект, способный захватывать переменные в области видимости. Он имеет: список захвата ; необязательный набор параметров с необязательным конечным типом возвращаемого значения; и тело. Примеры списков захвата:
[]
— ничего не захватывает.[=]
— захват локальных объектов (локальных переменных, параметров) в области видимости по значению.[&]
— захват локальных объектов (локальных переменных, параметров) в области видимости по ссылке.[this]
— захватить this
по ссылке.[a, &b]
— захват объектов a
по значению, b
по ссылке. int x = 1 ;
auto getX = [=] { return x; };
getX (); // == 1
auto addX = [=]( int y) { return x + y; };
addX ( 1 ); // == 2
auto getXRef = [&]() -> int & { return x; };
getXRef (); // int& to `x`
По умолчанию захват значений не может быть изменен внутри лямбды, поскольку метод, сгенерированный компилятором, помечен как const
. Ключевое слово mutable
позволяет изменять захваченные переменные. Ключевое слово помещается после списка параметров (который должен присутствовать, даже если он пуст).
int x = 1 ;
auto f1 = [&x] { x = 2 ; }; // OK: x is a reference and modifies the original
auto f2 = [x] { x = 2 ; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x]() mutable { x = 2 ; }; // OK: the lambda can perform any operations on the captured value
decltype
— это оператор, который возвращает объявленный тип переданного ему выражения. cv-квалификаторы и ссылки сохраняются, если они являются частью выражения. Примеры decltype
:
int a = 1 ; // `a` is declared as type `int`
decltype (a) b = a; // `decltype(a)` is `int`
const int & c = a; // `c` is declared as type `const int&`
decltype (c) d = a; // `decltype(c)` is `const int&`
decltype ( 123 ) e = 123; // `decltype(123)` is `int`
int && f = 1 ; // `f` is declared as type `int&&`
decltype (f) g = 1; // `decltype(f) is `int&&`
decltype ((a)) h = g; // `decltype((a))` is int&
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2.0 ); // `decltype(x + y)` => `decltype(3.0)` => `double`
См. также: decltype(auto) (C++14)
.
Семантически аналогично использованию typedef
, однако псевдонимы типов с using
легче читаются и совместимы с шаблонами.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
В C++11 представлен новый тип нулевого указателя, предназначенный для замены макроса NULL
в C. Сам nullptr
имеет тип std::nullptr_t
и может быть неявно преобразован в типы указателей и, в отличие от NULL
, не может быть преобразован в целочисленные типы, кроме bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Типобезопасные перечисления, которые решают множество проблем с перечислениями в стиле C, включая: неявные преобразования, невозможность указать базовый тип, загрязнение области видимости.
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
Атрибуты обеспечивают универсальный синтаксис по сравнению с __attribute__(...)
, __declspec
и т. Д.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Постоянные выражения-это выражения, которые , возможно, оцениваются компилятором во время компиляции. Только некомплексные вычисления могут быть выполнены в постоянном выражении (эти правила постепенно расслаблены в более поздних версиях). Используйте спецификатор constexpr
, чтобы указать переменную, функцию и т. Д. - это постоянное выражение.
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
В предыдущем фрагменте обратите внимание, что вычисление при вызове square
выполняется во время компиляции, а затем результат встроен в генерацию кода, а square2
вызывается во время выполнения.
Значения constexpr
-это те, которые компилятор может оценить, но не гарантируется, во время компиляции:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Постоянные выражения с классами:
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
Конструкторы теперь могут вызывать другие конструкторы в том же классе, используя список инициализаторов.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Пользовательские литералы позволяют вам расширить язык и добавлять свой собственный синтаксис. Чтобы создать буквальный, определите T operator "" X(...) { ... }
функция, которая возвращает тип T
с именем X
. Обратите внимание, что имя этой функции определяет имя буквального. Любые буквальные имена, не начинающиеся с подчеркивания, зарезервированы и не будут вызваны. Существуют правила о том, какие параметры следует принимать пользовательская буквальная функция, в соответствии с тем, какой тип вызывает буквальный.
Преобразование Цельсия в Фаренгейт:
// `unsigned long long` parameter required for integer literal.
long long operator " " _celsius( unsigned long long tempCelsius) {
return std::llround (tempCelsius * 1.8 + 32 );
}
24_celsius; // == 75
Строка в целочисленное преобразование:
// `const char*` and `std::size_t` required as parameters.
int operator " " _int( const char * str, std:: size_t ) {
return std::stoi (str);
}
" 123 " _int; // == 123, with type `int`
Указывает, что виртуальная функция переопределяет другую виртуальную функцию. Если виртуальная функция не переопределяет виртуальную функцию родителя, бросает ошибку компилятора.
struct A {
virtual void foo ();
void bar ();
};
struct B : A {
void foo () override ; // correct -- B::foo overrides A::foo
void bar () override ; // error -- A::bar is not virtual
void baz () override ; // error -- B::baz does not override A::baz
};
Указывает, что виртуальная функция не может быть переопределена в производном классе или что класс не может быть унаследован от.
struct A {
virtual void foo ();
};
struct B : A {
virtual void foo () final ;
};
struct C : B {
virtual void foo (); // error -- declaration of 'foo' overrides a 'final' function
};
Класс не может быть унаследован от.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Более элегантный, эффективный способ обеспечить реализацию функции по умолчанию, такую как конструктор.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
С наследством:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Более элегантный, эффективный способ обеспечить удаленную реализацию функции. Полезно для предотвращения копий на объектах.
class A {
int x;
public:
A ( int x) : x{x} {};
A ( const A&) = delete ;
A& operator =( const A&) = delete ;
};
A x { 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted
Синтаксический сахар для итерации над элементами контейнера.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Обратите внимание на разницу при использовании int
а не int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
Оператор Copy Constructor and Copy Assignment вызываются при создании копий, а с введением C ++ 11 семантики перемещения теперь существует конструктор движения и оператор назначения перемещения для перемещения.
struct A {
std::string s;
A () : s{ " test " } {}
A ( const A& o) : s{o. s } {}
A (A&& o) : s{ std::move (o. s )} {}
A& operator =(A&& o) {
s = std::move (o. s );
return * this ;
}
};
A f (A a) {
return a;
}
A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary
Преобразование конструкторов будет преобразовать значения синтаксиса списков в аргументы конструктора.
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
};
A a { 0 , 0 }; // calls A::A(int, int)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(int, int)
A d { 0 , 0 , 0 }; // calls A::A(int, int, int)
Обратите внимание, что синтаксис списков. Не позволяет сузить:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Обратите внимание, что если конструктор принимает std::initializer_list
, вместо этого он будет вызван:
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
A (std::initializer_list< int >) {}
};
A a { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A d { 0 , 0 , 0 }; // calls A::A(std::initializer_list<int>)
Функции преобразования теперь могут быть сделаны явными с помощью explicit
спецификатора.
struct A {
operator bool () const { return true ; }
};
struct B {
explicit operator bool () const { return true ; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
Все члены встроенного пространства имен обращаются так, как если бы они были частью его родительского пространства имен, позволяя специализации функций и облегчая процесс управления версиями. Это транзитивное свойство, если A содержит B, который, в свою очередь, содержит C, а B и C являются встроенными пространствами имен, члены C можно использовать так, как если бы они были на A.
namespace Program {
namespace Version1 {
int getVersion () { return 1 ; }
bool isFirstVersion () { return true ; }
}
inline namespace Version2 {
int getVersion () { return 2 ; }
}
}
int version { Program::getVersion ()}; // Uses getVersion() from Version2
int oldVersion { Program::Version1::getVersion ()}; // Uses getVersion() from Version1
bool firstVersion { Program::isFirstVersion ()}; // Does not compile when Version2 is added
Позволяет нестатическим членам данных инициализироваться там, где они объявлены, потенциально очищая конструкторы инициализации по умолчанию.
// Default initialization prior to C++11
class Human {
Human () : age{ 0 } {}
private:
unsigned age;
};
// Default initialization on C++11
class Human {
private:
unsigned age { 0 };
};
C ++ 11 теперь способен вывести вывод, когда серия прямых кронштейнов используется в качестве оператора или в качестве заключительного оператора Typedef, без необходимости добавления пробелов.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
Функции участников теперь могут быть квалифицированы в зависимости от того, является ли *this
ссылка на LVALUE или RVALUE.
struct Bar {
// ...
};
struct Foo {
Bar& getBar () & { return bar; }
const Bar& getBar () const & { return bar; }
Bar&& getBar() && { return std::move (bar); }
const Bar&& getBar() const && { return std::move (bar); }
private:
Bar bar;
};
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
C ++ 11 позволяет функциям и лямбдам альтернативный синтаксис для определения их типов возврата.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Эта функция особенно полезна, когда определенные типы возврата не могут быть разрешены:
// NOTE: This does not compile!
template < typename T, typename U>
decltype (a + b) add(T a, U b) {
return a + b;
}
// Trailing return types allows this:
template < typename T, typename U>
auto add (T a, U b) -> decltype(a + b) {
return a + b;
}
В C ++ 14, вместо этого можно использовать decltype(auto) (C++14)
.
Specifier noexcept
указывает, может ли функция бросить исключения. Это улучшенная версия throw()
.
void func1 () noexcept ; // does not throw
void func2 () noexcept ( true ); // does not throw
void func3 () throw(); // does not throw
void func4 () noexcept ( false ); // may throw
Функциям, не обращающимся на развороты, разрешается вызывать потенциально-измененные функции. Всякий раз, когда исключено, и поиск обработчика сталкивается с самым внешним блоком функции, не связанной с прохождением, вызывается функция std :: verminate.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Предоставляет стандартные типы для представления строк UTF-8.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11 представляет новый способ объявить строковые литералы как «необработанные струнные литералы». Символы, выпущенные из последовательности побега (вкладки, линейные каналы, отдельные обратные сшивки и т. Д.), Могут быть введены необработанные при сохранении форматирования. Это полезно, например, написать литературный текст, который может содержать много цитат или специального форматирования. Это может облегчить чтение и поддержание ваших струнных литералов.
Литерал необработанной строки объявляется с использованием следующего синтаксиса:
R"delimiter(raw_characters)delimiter"
где:
delimiter
- это необязательная последовательность символов, сделанных из любого исходного символа, кроме скобок, обратных сбоев и пространств.raw_characters
- это любая необработанная последовательность символов; не должен содержать закрывающуюся последовательность ")delimiter"
.Пример:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
указывает на то, что объект, переданный ему, может перенести свои ресурсы. Использование объектов, из которых были перемещены, следует использовать с осторожностью, так как их можно оставить в неуказанном состоянии (см.: Что я могу сделать с объектом перемещенного из-за?).
Определение std::move
(выполнение хода - это не что иное, как подставка для ссылки на RVALUE):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Передача std::unique_ptr
s:
std::unique_ptr< int > p1 { new int { 0 }}; // in practice, use std::make_unique
std::unique_ptr< int > p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr< int > p3 = std::move(p1); // move `p1` into `p3`
// now unsafe to dereference object held by `p1`
Возвращает аргументы, переданные ему, сохраняя их категорию стоимости и резюме. Полезно для общего кода и фабрик. Используется в сочетании с forwarding references
.
Определение std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Пример wrapper
функции, которая просто пересылает другие A
на новый конструктор копии или перемещения A
:
struct A {
A () = default ;
A ( const A& o) { std::cout << " copied " << std::endl; }
A (A&& o) { std::cout << " moved " << std::endl; }
};
template < typename T>
A wrapper (T&& arg) {
return A{std::forward<T>(arg)};
}
wrapper (A{}); // moved
A a;
wrapper (a); // copied
wrapper (std::move(a)); // moved
См. Также: forwarding references
, rvalue references
.
Библиотека std::thread
обеспечивает стандартный способ управления потоками, такими как нерест и убийство их. В приведенном ниже примере появляются несколько потоков, чтобы сделать разные вычисления, а затем программа ждет, пока все они закончат.
void foo ( bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true ); // thread will run foo(true)
for ( auto & thread : threadsVector) {
thread. join (); // Wait for threads to finish
}
Преобразует цифровой аргумент в std::string
.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Тип признаков определяет интерфейс на основе шаблона с компиляцией для запроса или изменения свойств типов.
static_assert (std::is_integral< int >::value);
static_assert (std::is_same< int , int >::value);
static_assert (std::is_same<std::conditional< true , int , double >::type, int >::value);
C ++ 11 представляет новые умные указатели: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
теперь становится устаревшим, а затем в конечном итоге удаляется в C ++ 17.
std::unique_ptr
-это необратимый, подвижный указатель, который управляет собственной памятью с помощью кучи. ПРИМЕЧАНИЕ. Предпочитайте использование функций std::make_X
Helper, а не использование конструкторов. Смотрите разделы для std :: make_unique и std :: make_shared.
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
if (p1) {
p1-> bar ();
}
{
std::unique_ptr<Foo> p2 { std::move (p1)}; // Now `p2` owns `Foo`
f (*p2);
p1 = std::move (p2); // Ownership returns to `p1` -- `p2` gets destroyed
}
if (p1) {
p1-> bar ();
}
// `Foo` instance is destroyed when `p1` goes out of scope
std::shared_ptr
- это умный указатель, который управляет ресурсом, который разделен на нескольких владельцах. Общий указатель содержит управляющий блок , в котором есть несколько компонентов, таких как управляемый объект и контрольный счетчик. Весь доступ к управляющему блоку безопасен, однако, манипулирование самого управляемого объекта не безопасен.
void foo (std::shared_ptr<T> t) {
// Do something with `t`...
}
void bar (std::shared_ptr<T> t) {
// Do something with `t`...
}
void baz (std::shared_ptr<T> t) {
// Do something with `t`...
}
std::shared_ptr<T> p1 { new T{}};
// Perhaps these take place in another threads?
foo (p1);
bar (p1);
baz (p1);
Библиотека Chrono содержит набор функций утилиты и типов, которые касаются продолжительности , часов и моментов времени . Одним из случаев использования этой библиотеки является код сравнительного анализа:
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration< double > elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
ПУЛЕЙ-это коллекция неподвижных гетерогенных значений. Получите доступ к элементам std::tuple
, распаковываясь с помощью std::tie
или используя std::get
.
// `playerProfile` has type `std::tuple<int, const char*, const char*>`.
auto playerProfile = std::make_tuple( 51 , " Frans Nielsen " , " NYI " );
std::get< 0 >(playerProfile); // 51
std::get< 1 >(playerProfile); // "Frans Nielsen"
std::get< 2 >(playerProfile); // "NYI"
Создает кортеж с ссылками LVALUE. Полезно для распаковки std::pair
и std::tuple
-объекты. Используйте std::ignore
как заполнитель для игнорируемых значений. В C ++ 17 вместо этого следует использовать структурированные привязки.
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
-это контейнер, построенный на вершине массива в стиле C. Поддерживает общие контейнерные операции, такие как сортировка.
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
Эти контейнеры сохраняют среднюю сложность постоянного времени для поиска, вставки и удаления операций. Чтобы достичь постоянной сложности, жертвует заказ на скорость, хэшируя элементы в ведра. Есть четыре неупорядоченных контейнера:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
- это рекомендуемый способ создать экземпляры std::shared_ptr
s по следующей причине:
new
оператора.foo
, как это: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
Компилятор может свободно вызывать new T{}
, затем function_that_throws()
и т. Д. ... поскольку мы выделили данные о куче при первой конструкции T
, мы представили здесь утечку. С std::make_shared
нам дают исключение-безопасность:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
мы должны выделить память для T
, затем в общем указателе мы должны выделять память для управляющего блока в указателе. См. Раздел «Умные указатели» для получения дополнительной информации о std::unique_ptr
и std::shared_ptr
.
std::ref(val)
используется для создания объекта типа std::reference_wrapper
, который содержит ссылку на Val. Используется в случаях, когда обычная ссылка прохождение с использованием &
не компилируется или &
отбрасывается из -за вычета типа. std::cref
похож, но созданная эталонная обертка содержит константную ссылку на Val.
// create a container to store reference of objects.
auto val = 99 ;
auto _ref = std::ref(val);
_ref++;
auto _cref = std::cref(val);
// _cref++; does not compile
std::vector<std::reference_wrapper< int >>vec; // vector<int&>vec does not compile
vec.push_back(_ref); // vec.push_back(&i) does not compile
cout << val << endl; // prints 100
cout << vec[ 0 ] << endl; // prints 100
cout << _cref; // prints 100
C ++ 11 представляет модель памяти для C ++, что означает поддержку библиотеки потоков и атомных операций. Некоторые из этих операций включают (но не ограничены) атомные нагрузки/магазины, сравнение и заставку, атомные флаги, обещания, фьючерсы, замки и переменные условия.
Смотрите разделы на: Std :: Thread
std::async
запускает данную функцию либо асинхронно, либо лениво оцениваемой, затем возвращает std::future
, которая удерживает результат этого вызова функции.
Первым параметром является политика, которая может быть:
std::launch::async | std::launch::deferred
Реализация зависит от того, выполнять ли асинхронное выполнение или ленивую оценку.std::launch::async
Запустите вызовный объект в новом потоке.std::launch::deferred
Выполните ленивую оценку в текущем потоке. int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
std::begin
и std::end
Free Fuctions были добавлены для возврата и итераторов контейнера. Эти функции также работают с необработанными массивами, которые не имеют функций begin
и end
элемента.
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
Энтони Каландра
См.: Https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
Массачусетский технологический институт