C++20 incluye las siguientes características de lenguaje nuevas:
C++20 incluye las siguientes características nuevas de biblioteca:
C++17 incluye las siguientes características de lenguaje nuevas:
C++17 incluye las siguientes características nuevas de biblioteca:
C++14 incluye las siguientes características de lenguaje nuevas:
C++14 incluye las siguientes características nuevas de biblioteca:
C++ 11 incluye las siguientes características de lenguaje nuevas:
C++ 11 incluye las siguientes características nuevas de biblioteca:
Nota: Si bien estos ejemplos ilustran cómo usar corrutinas en un nivel básico, suceden muchas más cosas cuando se compila el código. Estos ejemplos no pretenden cubrir completamente las rutinas de C++20. Dado que la biblioteca estándar aún no proporciona el
generator
y las clasestask
, utilicé la biblioteca cppcoro para compilar estos ejemplos.
Las corrutinas son funciones especiales cuya ejecución puede suspenderse y reanudarse. Para definir una corrutina, las palabras clave co_return
, co_await
o co_yield
deben estar presentes en el cuerpo de la función. Las corrutinas de C++20 no tienen pila; a menos que el compilador lo optimice, su estado se asigna en el montón.
Un ejemplo de corrutina es una función generadora , que produce (es decir, genera) un valor en cada invocación:
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;
}
La función de generador range
anterior genera valores desde el start
hasta end
(exclusivo), y cada paso de iteración produce el valor actual almacenado en start
. El generador mantiene su estado en cada invocación de range
(en este caso, la invocación es para cada iteración del bucle for). co_yield
toma la expresión dada, produce (es decir, devuelve) su valor y suspende la rutina en ese punto. Al reanudarse, la ejecución continúa después del co_yield
.
Otro ejemplo de corrutina es una tarea , que es un cálculo asincrónico que se ejecuta cuando se espera la tarea:
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;
}
En este ejemplo, se introduce la palabra clave co_await
. Esta palabra clave toma una expresión y suspende la ejecución si lo que está esperando (en este caso, la lectura o escritura) no está listo; de lo contrario, continúa la ejecución. (Tenga en cuenta que, bajo el capó, co_yield
usa co_await
).
Usar una tarea para evaluar perezosamente un valor:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Los conceptos se denominan predicados en tiempo de compilación que restringen los tipos. Toman la siguiente forma:
template < template-parameter-list >
concept concept-name = constraint-expression;
donde constraint-expression
se evalúa como un booleano constexpr. Las restricciones deben modelar requisitos semánticos, como por ejemplo si un tipo es numérico o hash. Se produce un error del compilador si un tipo determinado no satisface el concepto al que está vinculado (es decir, constraint-expression
devuelve false
). Debido a que las restricciones se evalúan en tiempo de compilación, pueden proporcionar mensajes de error más significativos y seguridad en tiempo de ejecución.
// `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>;
Hay una variedad de formas sintácticas para hacer cumplir conceptos:
// 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> () {
// ...
};
La palabra clave requires
se usa para iniciar una cláusula requires
o una expresión 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;
}
Tenga en cuenta que la lista de parámetros en una expresión requires
es opcional. Cada requisito en una expresión requires
es uno de los siguientes:
template < typename T>
concept callable = requires (T f) { f (); };
typename
seguida de un nombre de tipo, afirma que el nombre de tipo dado es válido. 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
, especifican restricciones adicionales (como las de los argumentos de parámetros locales). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
Ver también: biblioteca de conceptos.
C++ 20 presenta el operador de nave espacial ( <=>
) como una nueva forma de escribir funciones de comparación que reducen el texto estándar y ayudan a los desarrolladores a definir una semántica de comparación más clara. La definición de un operador de comparación de tres vías generará automáticamente las otras funciones del operador de comparación (es decir, ==
, !=
, <
, etc.).
Se introducen tres ordenamientos:
std::strong_ordering
: El orden fuerte distingue entre elementos iguales (idénticos e intercambiables). Proporciona ordenamiento less
, greater
, equivalent
e equal
. Ejemplos de comparaciones: búsqueda de un valor específico en una lista, valores de números enteros, cadenas que distinguen entre mayúsculas y minúsculas.std::weak_ordering
: El orden débil distingue entre elementos que son equivalentes (no idénticos, pero pueden ser intercambiables a efectos de comparación). Proporciona pedidos less
, greater
y equivalent
. Ejemplos de comparaciones: cadenas que no distinguen entre mayúsculas y minúsculas, ordenar, comparar algunos, pero no todos, miembros visibles de una clase.std::partial_ordering
: El ordenamiento parcial sigue el mismo principio de ordenamiento débil pero incluye el caso en el que un ordenamiento no es posible. Proporciona ordenamiento less
, greater
, equivalent
y unordered
. Ejemplos de comparaciones: valores de punto flotante (por ejemplo, NaN
).Un operador de comparación de tres vías predeterminado realiza una comparación entre miembros:
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
También puedes definir tus propias comparaciones:
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
Sintaxis del inicializador designado estilo C. Todos los campos miembro que no aparecen explícitamente en la lista de inicializadores designados se inicializan de forma predeterminada.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Utilice una sintaxis de plantilla familiar en expresiones lambda.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Esta característica simplifica los patrones de código comunes, ayuda a mantener los alcances ajustados y ofrece una solución elegante a un problema común de por vida.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Proporciona una pista al optimizador de que la declaración etiquetada tiene una alta probabilidad de ser ejecutada.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Si uno de los atributos probable/improbable aparece después del paréntesis derecho de una declaración if, indica que es probable/improbable que la rama ejecute su subdeclaración (cuerpo).
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
También se puede aplicar a la subsentencia (cuerpo) de una declaración de iteración.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
La captura implícita this
en una captura lambda usando [=]
ahora está en desuso; prefiera capturar explícitamente usando [=, this]
o [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
Las clases ahora se pueden usar en parámetros de plantilla que no sean de tipo. Los objetos pasados como argumentos de plantilla tienen el tipo const T
, donde T
es el tipo de objeto y tienen una duración de almacenamiento estático.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
Las funciones virtuales ahora se pueden constexpr
y evaluar en tiempo de compilación. Las funciones virtuales constexpr
pueden anular funciones virtuales que no son constexpr
y viceversa.
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
Seleccione condicionalmente en tiempo de compilación si un constructor se hace explícito o no. explicit(true)
es lo mismo que especificar 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
Similar a las funciones constexpr
, pero las funciones con un especificador consteval
deben producir una constante. Éstas se denominan 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
Incluya los miembros de una enumeración en el alcance para mejorar la legibilidad. Antes:
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 " ;
}
}
Después:
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 " ;
}
}
Capture paquetes de parámetros por valor:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Capture paquetes de parámetros por referencia:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Proporciona un tipo estándar para representar cadenas UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
El especificador constinit
requiere que una variable se inicialice en tiempo de compilación.
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.
Ayuda a admitir macros variadas al evaluar el argumento dado si la macro variada no está vacía.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
La biblioteca estándar también proporciona conceptos para construir conceptos más complicados. Algunos de estos incluyen:
Conceptos básicos del lenguaje:
same_as
: especifica que dos tipos son iguales.derived_from
: especifica que un tipo se deriva de otro tipo.convertible_to
: especifica que un tipo es implícitamente convertible a otro tipo.common_with
: especifica que dos tipos comparten un tipo común.integral
: especifica que un tipo es un tipo integral.default_constructible
: especifica que un objeto de un tipo se puede construir de forma predeterminada.Conceptos de comparación:
boolean
: especifica que un tipo se puede utilizar en contextos booleanos.equality_comparable
: especifica que operator==
es una relación de equivalencia.Conceptos de objeto:
movable
: especifica que un objeto de un tipo se puede mover e intercambiar.copyable
: especifica que un objeto de un tipo se puede copiar, mover e intercambiar.semiregular
: especifica que un objeto de un tipo se puede copiar, mover, intercambiar y construir de forma predeterminada.regular
: especifica que un tipo es regular , es decir, es semiregular
e equality_comparable
.Conceptos invocables:
invocable
: especifica que se puede invocar un tipo invocable con un conjunto determinado de tipos de argumentos.predicate
: especifica que un tipo invocable es un predicado booleano.Ver también: conceptos.
Combine la simplicidad de printf
con la seguridad de tipos de iostream
. Utiliza llaves como marcadores de posición y admite formatos personalizados similares a los especificadores de estilo 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);
}
Para dar formato a tipos personalizados:
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"
Almacena en buffer las operaciones de salida para el flujo de salida empaquetado asegurando la sincronización (es decir, sin entrelazado de salida).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Un tramo es una vista (es decir, no propietaria) de un contenedor que proporciona acceso controlado por límites a un grupo contiguo de elementos. Dado que las vistas no poseen sus elementos, es barato construirlas y copiarlas; una forma simplificada de pensar en las vistas es que contienen referencias a sus datos. A diferencia de mantener un puntero/iterador y un campo de longitud, un intervalo envuelve ambos en un solo objeto.
Los tramos pueden tener un tamaño dinámico o fijo (lo que se conoce como extensión ). Los tramos de tamaño fijo se benefician de la verificación de límites.
Span no propaga const, por lo que para construir un span de solo lectura use std::span<const T>
.
Ejemplo: usar un intervalo de tamaño dinámico para imprimir números enteros de varios contenedores.
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.
Ejemplo: un intervalo de tamaño estático no se podrá compilar para contenedores que no coincidan con la extensión del intervalo.
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 proporciona un nuevo encabezado <bit>
que proporciona algunas operaciones de bits, incluido popcount.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Constantes matemáticas que incluyen PI, número de Euler, etc. definidas en el encabezado <numbers>
.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Función de predicado que es veraz cuando se llama en un contexto de tiempo de compilación.
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]`
Las cadenas (y las vistas de cadenas) ahora tienen las funciones miembro starts_with
y ends_with
para comprobar si una cadena comienza o termina con la cadena dada.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Los contenedores asociativos, como conjuntos y mapas, tienen una función miembro contains
, que se puede utilizar en lugar del modismo "buscar y comprobar el final del iterador".
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
Una forma más segura de reinterpretar un objeto de un tipo a otro.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Calcula el punto medio de dos números enteros de forma segura (sin desbordamiento).
std::midpoint ( 1 , 3 ); // == 2
Convierte la matriz/objeto "similar a una matriz" dada en un 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>`
Vincula los primeros N argumentos (donde N es el número de argumentos después de la función dada a std::bind_front
) a una función libre, lambda o función miembro determinada.
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
Proporciona std::erase
y/o std::erase_if
para una variedad de contenedores STL como cadenas, listas, vectores, mapas, etc.
Para borrar por valor use std::erase
, o para especificar un predicado cuando borrar elementos use std::erase_if
. Ambas funciones devuelven el número de elementos borrados.
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}
Funciones auxiliares para dar nombres a los resultados de la comparación:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
Ver también: comparación de tres vías.
Compara lexicográficamente dos rangos mediante una comparación de tres vías y produce un resultado del tipo de categoría de comparación aplicable más fuerte.
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
Ver también: comparación de tres vías, ayudas de comparación de tres vías.
Deducción automática de argumentos de plantilla muy similar a cómo se hace para las funciones, pero ahora incluye constructores de clases.
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>
Siguiendo las reglas de deducción de auto
, respetando la lista de parámetros de plantilla que no son de tipo de tipos permitidos[*], los argumentos de la plantilla se pueden deducir de los tipos de sus argumentos:
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 >();
* - Por ejemplo, no puede usar un double
como tipo de parámetro de plantilla, lo que también hace que esta deducción no sea válida usando auto
.
Una expresión de plegado realiza un plegado de un paquete de parámetros de plantilla sobre un operador binario.
(... op e)
o (e op ...)
, donde op
es un operador de pliegue y e
es un paquete de parámetros no expandido, se denomina pliegues unarios .(e1 op ... op e2)
, donde op
son operadores de plegado, se llama plegado binario . Tanto e1
como e2
son un paquete de parámetros no expandido, pero no ambos. 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
Cambios en la deducción auto
cuando se usa con la sintaxis de inicialización uniforme. Anteriormente, auto x {3};
deduce un std::initializer_list<int>
, que ahora deduce a 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
Lambdas en tiempo de compilación usando 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
por valor Anteriormente, capturar this
en el entorno de una lambda era solo de referencia. Un ejemplo de dónde esto es problemático es el código asincrónico que utiliza devoluciones de llamada que requieren que un objeto esté disponible, potencialmente después de su vida útil. *this
(C++17) ahora hará una copia del objeto actual, mientras this
(C++11) continúa capturando por referencia.
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
El especificador en línea se puede aplicar tanto a variables como a funciones. Una variable declarada en línea tiene la misma semántica que una función declarada en línea.
// 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
También se puede utilizar para declarar y definir una variable miembro estática, de modo que no sea necesario inicializarla en el archivo fuente.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Usar el operador de resolución de espacios de nombres para crear definiciones de espacios de nombres anidados.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
El código anterior se puede escribir así:
namespace A ::B::C {
int i;
}
Una propuesta para la inicialización desestructurante, que permitiría escribir auto [ x, y, z ] = expr;
donde el tipo de expr
era un objeto similar a una tupla, cuyos elementos estarían vinculados a las variables x
, y
y z
(que declara esta construcción). Los objetos tipo tupla incluyen std::tuple
, std::pair
, std::array
y estructuras agregadas.
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
}
Nuevas versiones de las declaraciones if
y switch
que simplifican los patrones de código comunes y ayudan a los usuarios a mantener los alcances ajustados.
{
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 ());
}
Escriba código cuya instancia se cree según una condición de tiempo de compilación.
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);
Un carácter literal que comienza con u8
es un carácter literal de tipo char
. El valor de un literal de carácter UTF-8 es igual a su valor de punto de código ISO 10646.
char x = u8 ' x ' ;
Las enumeraciones ahora se pueden inicializar usando sintaxis entre llaves.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++ 17 introduce tres nuevos atributos: [[fallthrough]]
, [[nodiscard]]
y [[maybe_unused]]
.
[[fallthrough]]
indica al compilador que fallar en una declaración de cambio es el comportamiento previsto. Este atributo solo puede usarse en una declaración de cambio y debe colocarse antes de la siguiente etiqueta de caso/predeterminada. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
emite una advertencia cuando una función o clase tiene este atributo y su valor de retorno se descarta. [[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]]
indica al compilador que una variable o parámetro podría no usarse y está previsto. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
El operador __has_include (operand)
se puede utilizar en las expresiones #if
y #elif
para comprobar si un encabezado o archivo fuente ( operand
) está disponible para su inclusión o no.
Un caso de uso de esto sería usar dos bibliotecas que funcionen de la misma manera, usando la de respaldo/experimental si la preferida no se encuentra en el sistema.
# 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
También se puede utilizar para incluir encabezados existentes con diferentes nombres o ubicaciones en varias plataformas, sin saber en qué plataforma se está ejecutando el programa. Los encabezados OpenGL son un buen ejemplo de esto y se encuentran en el directorio OpenGL
en macOS y GL
en otros. plataformas.
# 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
La deducción de argumentos de plantilla de clase (CTAD) permite al compilador deducir argumentos de plantilla a partir de los argumentos del constructor.
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>*
Para los tipos definidos por el usuario, se pueden utilizar guías de deducción para guiar al compilador sobre cómo deducir los argumentos de la plantilla, si corresponde:
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
La plantilla de clase std::variant
representa una union
con seguridad de tipos. Una instancia de std::variant
en un momento dado tiene un valor de uno de sus tipos alternativos (también es posible que no tenga valor).
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
La plantilla de clase std::optional
gestiona un valor contenido opcional, es decir, un valor que puede estar presente o no. Un caso de uso común para opcional es el valor de retorno de una función que puede fallar.
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 )) {
// ...
}
Un contenedor con seguridad de tipos para valores únicos de cualquier tipo.
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
Una referencia no propietaria a una cadena. Útil para proporcionar una abstracción encima de cadenas (por ejemplo, para análisis).
// 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"
Invocar un objeto Callable
con parámetros. Ejemplos de objetos invocables son std::function
o lambdas; objetos que se pueden llamar de manera similar a una función normal.
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
Invoca un objeto Callable
con una tupla de argumentos.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
La nueva biblioteca std::filesystem
proporciona una forma estándar de manipular archivos, directorios y rutas en un sistema de archivos.
Aquí, un archivo grande se copia en una ruta temporal si hay espacio disponible:
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 " ));
}
}
El nuevo tipo std::byte
proporciona una forma estándar de representar datos como un byte. Los beneficios de usar std::byte
sobre char
o unsigned char
es que no es un tipo de carácter y tampoco es un tipo aritmético; mientras que las únicas sobrecargas de operadores disponibles son las operaciones bit a bit.
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
Tenga en cuenta que std::byte
es simplemente una enumeración, y la inicialización entre llaves de enumeraciones es posible gracias a la inicialización directa de listas de enumeraciones.
Mover nodos y fusionar contenedores sin la sobrecarga de costosas copias, movimientos o asignaciones/desasignaciones de montón.
Mover elementos de un mapa a otro:
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" } };
Insertar un conjunto completo:
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 }
Insertar elementos que sobrevivan al contenedor:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Cambiar la clave de un elemento del mapa:
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" } }
Muchos de los algoritmos STL, como los métodos copy
, find
y sort
, comenzaron a soportar las políticas de ejecución paralela : seq
, par
y par_unseq
que se traducen como "secuencialmente", "paralelo" y "paralelo sin secuenciar".
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));
Muestra n elementos en la secuencia dada (sin reemplazo) donde cada elemento tiene las mismas posibilidades de ser seleccionado.
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
Sujete el valor dado entre un límite superior e inferior.
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
Doblar una gama determinada de elementos. Conceptualmente similar a std::accumulate
, pero std::reduce
realizará el pliegue en paralelo. Debido a que el pliegue se realiza en paralelo, si especifica una operación binaria, se requiere que sea asociativa y conmutativa. Una operación binaria determinada tampoco debe cambiar ningún elemento ni invalidar ningún iterador dentro del rango dado.
La operación binaria predeterminada es std::plus con un valor inicial de 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
Además, puede especificar transformaciones para reductores:
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
Soporte para sumas de prefijos (tanto escaneos inclusivos como exclusivos) junto con transformaciones.
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
Máximo común divisor (MCD) y mínimo común múltiplo (MCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Función de utilidad que devuelve la negación del resultado de la función dada.
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
Convierte integrales y flotantes en una cadena o viceversa. Las conversiones no se generan, no se asignan y son más seguras que las equivalentes de la biblioteca estándar de C.
Los usuarios son responsables de asignar suficiente almacenamiento requerido para std::to_chars
, o la función fallará al establecer el objeto de código de error en su valor de retorno.
Estas funciones le permiten pasar opcionalmente una base (el valor predeterminado es base-10) o un especificador de formato para la entrada de tipo flotante.
std::to_chars
devuelve un puntero de carácter (no constante) que está después del final de la cadena en la que escribió la función dentro del búfer dado y un objeto de código de error.std::from_chars
devuelve un puntero de carácter constante que, en caso de éxito, es igual al puntero final pasado a la función y un objeto de código de error.Ambos objetos de código de error devueltos por estas funciones son iguales al objeto de código de error inicializado de forma predeterminada en caso de éxito.
Convierte el número 123
en un 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 */ }
Convierta de un std::string
con valor "123"
a un número entero:
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 */ }
Proporciona funciones auxiliares de abdominales, redondos, techo y piso para std::chrono::duration
y 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
Los literales binarios proporcionan una manera conveniente de representar un número de base 2. Es posible separar dígitos con '
.
0b110 // == 6
0b1111'1111 // == 255
C++14 ahora permite el especificador de tipo auto
en la lista de parámetros, lo que habilita lambdas polimórficas.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Esto permite crear capturas lambda inicializadas con expresiones arbitrarias. El nombre dado al valor capturado no necesita estar relacionado con ninguna variable en los ámbitos adjuntos e introduce un nuevo nombre dentro del cuerpo lambda. La expresión de inicialización se evalúa cuando se crea la lambda (no cuando se invoca ).
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
Debido a que ahora es posible mover (o reenviar ) valores a una lambda que antes solo se podían capturar mediante copia o referencia, ahora podemos capturar tipos de solo movimiento en una lambda por valor. Tenga en cuenta que en el siguiente ejemplo, la p
en la lista de captura de task2
en el lado izquierdo de =
es una nueva variable privada del cuerpo lambda y no hace referencia a la p
original.
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
El uso de estas capturas de referencia puede tener nombres diferentes a los de la variable referenciada.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
Al utilizar un tipo de retorno auto
en C++ 14, el compilador intentará deducir el tipo por usted. Con lambdas, ahora puede deducir su tipo de retorno usando auto
, lo que hace posible devolver una referencia deducida o una referencia 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`
El especificador de tipo decltype(auto)
también deduce un tipo como lo hace auto
. Sin embargo, deduce los tipos de retorno manteniendo sus referencias y calificadores cv, mientras que auto
no lo hará.
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);
Ver también: decltype (C++11)
.
En C++11, los cuerpos de las funciones constexpr
solo podían contener un conjunto muy limitado de sintaxis, que incluye (entre otras): typedef
s, using
s y una única declaración return
. En C++ 14, el conjunto de sintaxis permitidas se expande enormemente para incluir la sintaxis más común, como sentencias if
, múltiples return
s, bucles, etc.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++ 14 permite crear plantillas para variables:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++ 14 introduce el atributo [[deprecated]]
para indicar que una unidad (función, clase, etc.) no está recomendada y probablemente genere advertencias de compilación. Si se proporciona un motivo, se incluirá en las advertencias.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Nuevos literales definidos por el usuario para tipos de biblioteca estándar, incluidos nuevos literales integrados para chrono
y basic_string
. Estos pueden ser constexpr
, lo que significa que se pueden usar en tiempo de compilación. Algunos usos de estos literales incluyen el análisis de enteros en tiempo de compilación, literales binarios y literales de números imaginarios.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
La plantilla de clase std::integer_sequence
representa una secuencia de números enteros en tiempo de compilación. Hay algunos ayudantes integrados en la parte superior:
std::make_integer_sequence<T, N>
- crea una secuencia de 0, ..., N - 1
con tipo T
.std::index_sequence_for<T...>
- convierte un paquete de parámetros de plantilla en una secuencia de números enteros.Convierta una matriz en una tupla:
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
es la forma recomendada de crear instancias de std::unique_ptr
s debido a las siguientes razones:
new
operador.foo
así: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
El compilador es libre de llamar new T{}
, luego function_that_throws()
, y así sucesivamente... Dado que hemos asignado datos en el montón en la primera construcción de un T
, hemos introducido una fuga aquí. Con std::make_unique
, se nos proporciona seguridad de excepción:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Consulte la sección sobre punteros inteligentes (C++11) para obtener más información sobre std::unique_ptr
y std::shared_ptr
.
Mover un objeto significa transferir la propiedad de algún recurso que gestiona a otro objeto.
El primer beneficio de la semántica de movimientos es la optimización del rendimiento. Cuando un objeto está a punto de llegar al final de su vida útil, ya sea porque es temporal o porque se llama explícitamente std::move
, un movimiento suele ser una forma más económica de transferir recursos. Por ejemplo, mover un std::vector
es simplemente copiar algunos punteros y el estado interno al nuevo vector; copiar implicaría tener que copiar cada uno de los elementos contenidos en el vector, lo cual es costoso e innecesario si el vector anterior pronto será eliminado. destruido.
Los movimientos también hacen posible que tipos no copiables como std::unique_ptr
s (punteros inteligentes) garanticen a nivel de lenguaje que solo haya una instancia de un recurso administrado a la vez, al tiempo que se puede transferir una instancia. entre alcances.
Consulte las secciones sobre: referencias de rvalue, funciones miembro especiales para la semántica de movimiento, std::move
, std::forward
, forwarding references
.
C++ 11 introduce una nueva referencia denominada referencia rvalue . Una referencia rvalue a T
, que es un parámetro de tipo que no es de plantilla (como int
, o un tipo definido por el usuario), se crea con la sintaxis T&&
. Las referencias de Rvalue solo se vinculan a rvalues.
Escriba la deducción con valores l y valores r:
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)
Ver también: std::move
, std::forward
, forwarding references
.
También conocidos (extraoficialmente) como referencias universales . Se crea una referencia de reenvío con la sintaxis T&&
donde T
es un parámetro de tipo de plantilla, o usando auto&&
. Esto permite un reenvío perfecto : la capacidad de pasar argumentos manteniendo su categoría de valor (por ejemplo, los valores l permanecen como valores l, los temporales se reenvían como valores r).
Las referencias de reenvío permiten que una referencia se vincule a un valor l o rvalue según el tipo. Las referencias de reenvío siguen las reglas de colapso de referencias :
T& &
se convierte en T&
T& &&
se convierte en T&
T&& &
se convierte en T&
T&& &&
se convierte en T&&
deducción de tipo auto
con valores l y valores r:
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`
Deducción de parámetros de tipo de plantilla con valores l y valores r:
// 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&&)
Ver también: std::move
, std::forward
, rvalue references
.
La sintaxis ...
crea un paquete de parámetros o lo expande. Un paquete de parámetros de plantilla es un parámetro de plantilla que acepta cero o más argumentos de plantilla (no tipos, tipos o plantillas). Una plantilla con al menos un paquete de parámetros se denomina plantilla variada .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Un uso interesante para esto es crear una lista de inicializadores a partir de un paquete de parámetros para iterar sobre argumentos de funciones variables.
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
Un contenedor ligero similar a una matriz de elementos creado utilizando una sintaxis de "lista entre llaves". Por ejemplo, { 1, 2, 3 }
crea una secuencia de números enteros, que tiene el tipo std::initializer_list<int>
. Útil como reemplazo para pasar un vector de objetos a una función.
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
Afirmaciones que se evalúan en tiempo de compilación.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
El compilador deduce las variables escritas auto
según el tipo de su inicializador.
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
Extremadamente útil para mejorar la legibilidad, especialmente para tipos complicados:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
Las funciones también pueden deducir el tipo de retorno usando auto
. En C++11, se debe especificar un tipo de retorno explícitamente o usando decltype
de la siguiente manera:
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
El tipo de retorno final en el ejemplo anterior es el tipo declarado (consulte la sección sobre decltype
) de la expresión x + y
. Por ejemplo, si x
es un número entero e y
es un doble, decltype(x + y)
es un doble. Por lo tanto, la función anterior deducirá el tipo dependiendo del tipo que produzca la expresión x + y
. Observe que el tipo de retorno final tiene acceso a sus parámetros, y this
cuando sea apropiado.
Una lambda
es un objeto de función sin nombre capaz de capturar variables dentro del alcance. Incluye: una lista de captura ; un conjunto opcional de parámetros con un tipo de retorno final opcional; y un cuerpo. Ejemplos de listas de captura:
[]
- no captura nada.[=]
- captura objetos locales (variables locales, parámetros) en el alcance por valor.[&]
- captura objetos locales (variables locales, parámetros) en el alcance por referencia.[this]
- captura this
por referencia.[a, &b]
- captura objetos a
por valor, b
por referencia. 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`
De forma predeterminada, las capturas de valores no se pueden modificar dentro de lambda porque el método generado por el compilador está marcado como const
. La palabra clave mutable
permite modificar las variables capturadas. La palabra clave se coloca después de la lista de parámetros (que debe estar presente incluso si está vacía).
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
es un operador que devuelve el tipo declarado de una expresión que se le pasa. Los calificadores cv y las referencias se mantienen si forman parte de la expresión. Ejemplos de 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`
Ver también: decltype(auto) (C++14)
.
Semánticamente similar al uso de typedef
sin embargo, los alias de tipo con using
son más fáciles de leer y compatibles con las plantillas.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++ 11 introduce un nuevo tipo de puntero nulo diseñado para reemplazar la macro NULL
de C. nullptr
en sí es de tipo std::nullptr_t
y se puede convertir implícitamente en tipos de puntero y, a diferencia de NULL
, no se puede convertir a tipos integrales excepto bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Enumeraciones con seguridad de tipos que resuelven una variedad de problemas con enumeraciones de estilo C, que incluyen: conversiones implícitas, incapacidad para especificar el tipo subyacente y contaminación del alcance.
// 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;
Los atributos proporcionan una sintaxis universal sobre __attribute__(...)
, __declspec
, etc.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Las expresiones constantes son expresiones que posiblemente son evaluadas por el compilador en el tiempo de compilación. Solo los cálculos no complejos se pueden llevar a cabo en una expresión constante (estas reglas se relajan progresivamente en versiones posteriores). Use el especificador constexpr
para indicar la variable, la función, etc. es una expresión constante.
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
En el fragmento anterior, observe que el cálculo cuando llame square
se lleva a cabo en el tiempo de compilación, y luego el resultado se incrusta en la generación de código, mientras que square2
se llama en el tiempo de ejecución.
Los valores constexpr
son aquellos que el compilador puede evaluar, pero no se garantiza, en el tiempo de compilación:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Expresiones constantes con clases:
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 );
Los constructores ahora pueden llamar a otros constructores en la misma clase utilizando una lista de inicializador.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Los literales definidos por el usuario le permiten extender el idioma y agregar su propia sintaxis. Para crear una función literal, defina un T operator "" X(...) { ... }
que devuelve un tipo T
, con un nombre X
. Tenga en cuenta que el nombre de esta función define el nombre del literal. Cualquier nombre literal que no comience con un subrayador se reserva y no se invocará. Hay reglas sobre qué parámetros debe aceptar una función literal definida por el usuario, de acuerdo con qué tipo se llama el literal.
Convertir Celsius a Fahrenheit:
// `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
Cadena a conversión entera:
// `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`
Especifica que una función virtual anula otra función virtual. Si la función virtual no anula la función virtual de un padre, lanza un error del compilador.
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
};
Especifica que una función virtual no se puede anular en una clase derivada o que una clase no se puede heredar.
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
};
La clase no puede ser heredada de.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Una forma más elegante y eficiente de proporcionar una implementación predeterminada de una función, como un constructor.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
Con herencia:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Una forma más elegante y eficiente de proporcionar una implementación eliminada de una función. Útil para prevenir copias en objetos.
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
Azúcar sintáctico para iterar sobre los elementos de un contenedor.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Tenga en cuenta la diferencia cuando se usa int
en lugar de int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
El constructor de copias y el operador de asignación de copias se llaman cuando se realizan copias, y con la introducción de C ++ 11 de la semántica de movimiento, ahora hay un constructor de movimiento y operador de asignación de movimiento para movimientos.
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
Los constructores de conversión convertirán los valores de la sintaxis de la lista arriesgada en argumentos de constructor.
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)
Tenga en cuenta que la sintaxis de la lista arriesgada no permite el estrechamiento:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Tenga en cuenta que si un constructor acepta un std::initializer_list
, se llamará en su lugar:
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>)
Las funciones de conversión ahora se pueden hacer explícitas utilizando el especificador 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()
Todos los miembros de un espacio de nombres en línea son tratados como si fueran parte de su espacio de nombres matriz, permitiendo la especialización de funciones y aliviar el proceso de versiones. Esta es una propiedad transitiva, si A contiene B, que a su vez contiene C y B y C son espacios de nombres en línea, los miembros de C pueden usarse como si estuvieran en 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
Permite que los miembros de datos no estatales se inicialicen donde se declaran, potencialmente limpiando a los constructores de inicializaciones predeterminadas.
// 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 ahora puede inferir cuando una serie de soportes de ángulo recto se usa como operador o como una declaración de cierre de Typedef, sin tener que agregar Whitespace.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
Las funciones de los miembros ahora se pueden calificar dependiendo de si *this
es una referencia de Lvalue o 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 permite funciones y lambdas una sintaxis alternativa para especificar sus tipos de devolución.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Esta característica es especialmente útil cuando ciertos tipos de retorno no se pueden resolver:
// 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;
}
En C ++ 14, decltype(auto) (C++14)
se puede usar en su lugar.
El especificador noexcept
especifica si una función podría arrojar excepciones. Es una versión mejorada de 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
Las funciones sin lanzamiento pueden llamar a funciones potencialmente lanzadas. Cada vez que se lanza una excepción y la búsqueda de un controlador encuentra el bloque más externo de una función sin lanzamiento, se llama a la función std :: terminado.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Proporciona tipos estándar para representar cadenas UTF-8.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11 introduce una nueva forma de declarar literales de cadena como "literales de cadenas crudas". Los caracteres emitidos desde una secuencia de escape (pestañas, alimentos de línea, barras de retroceso individuales, etc.) se pueden ingresar sin procesar mientras se preservan el formato. Esto es útil, por ejemplo, escribir texto literario, que podría contener muchas citas o formateo especial. Esto puede hacer que sus literales de cadena sean más fáciles de leer y mantener.
Se declara un literal de cadena sin procesar utilizando la siguiente sintaxis:
R"delimiter(raw_characters)delimiter"
dónde:
delimiter
es una secuencia opcional de caracteres hecha de cualquier carácter de origen, excepto paréntesis, barras de retroceso y espacios.raw_characters
es cualquier secuencia de personajes sin procesar; no debe contener la secuencia de cierre ")delimiter"
.Ejemplo:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
indica que el objeto que se le pasó a él puede transferir sus recursos. El uso de objetos que se han movido deben usarse con cuidado, ya que se pueden dejar en un estado no especificado (ver: ¿Qué puedo hacer con un objeto conmovido?).
Una definición de std::move
(realizar un movimiento no es más que lanzar a una referencia de RValue):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Transferir 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`
Devuelve los argumentos transmitidos a él mientras mantienen su categoría de valor y calificadores CV. Útil para código genérico y fábricas. Utilizado junto con forwarding references
.
Una definición de std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Un ejemplo de una wrapper
de funciones que solo reenvía a otros A
a un nuevo constructor de copia o mudanza de A
objeto:
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
Ver también: forwarding references
, rvalue references
.
La biblioteca std::thread
proporciona una forma estándar de controlar los hilos, como el desove y matarlos. En el ejemplo a continuación, se generan múltiples hilos para hacer diferentes cálculos y luego el programa espera a que todos los terminen.
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
}
Convierte un argumento numérico en una std::string
.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Los rasgos de tipo define una interfaz basada en plantillas de tiempo de compilación para consultar o modificar las propiedades de los tipos.
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 presenta nuevos consejos inteligentes: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
ahora se desacreza y luego finalmente se elimina en C ++ 17.
std::unique_ptr
es un puntero móvil no cotizable que administra su propia memoria alocada por montón. NOTA: Prefiera usar las funciones std::make_X
Helper en lugar de usar constructores. Vea las secciones para std :: make_unique y 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
Un std::shared_ptr
es un puntero inteligente que administra un recurso que se comparte entre múltiples propietarios. Un puntero compartido contiene un bloque de control que tiene algunos componentes, como el objeto administrado y un contador de referencia. Todo el acceso al bloque de control es seguro de subprocesos, sin embargo, manipular el objeto administrado en sí no es seguro de hilo.
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);
La biblioteca Chrono contiene un conjunto de funciones y tipos de utilidad que se ocupan de duraciones , relojes y puntos de tiempo . Un caso de uso de esta biblioteca es el código de evaluación comparativa:
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`
Las tuplas son una colección de tamaño fijo de valores heterogéneos. Acceda a los elementos de un std::tuple
desempacando usando std::tie
o usando 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"
Crea una tupla de referencias de Lvalue. Útil para desempacar los objetos std::pair
y std::tuple
. Use std::ignore
como un marcador de posición para valores ignorados. En C ++ 17, se deben usar enlaces estructurados.
// 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
es un contenedor construido sobre una matriz de estilo C. Admite operaciones comunes de contenedores como la clasificación.
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 }
Estos contenedores mantienen una complejidad promedio de tiempo constante para la búsqueda, insertar y eliminar operaciones. Para lograr la complejidad en el tiempo constante, los sacrificios de la velocidad de la velocidad de los elementos de hash en cubos. Hay cuatro contenedores desordenados:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
es la forma recomendada de crear instancias de std::shared_ptr
s debido a las siguientes razones:
new
operador.foo
así: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
El compilador es libre de llamar a new T{}
, luego function_that_throws()
, y así sucesivamente ... ya que hemos asignado datos sobre el montón en la primera construcción de una T
, hemos introducido una fuga aquí. Con std::make_shared
, se nos da seguridad de excepción:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
, tenemos que asignar memoria para T
, luego en el puntero compartido tenemos que asignar memoria para el bloque de control dentro del puntero. Consulte la sección sobre consejos inteligentes para obtener más información sobre std::unique_ptr
y std::shared_ptr
.
std::ref(val)
se usa para crear objeto de tipo std::reference_wrapper
que contiene referencia de Val. Se utiliza en los casos cuando la referencia habitual que pasa usando &
no compila &
se cae debido a la deducción de tipo. std::cref
es similar pero el envoltorio de referencia creado tiene una referencia const a 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 introduce un modelo de memoria para C ++, lo que significa soporte de biblioteca para subprocesos y operaciones atómicas. Algunas de estas operaciones incluyen (pero no se limitan a) cargas/tiendas atómicas, comparar y intercambiar, indicadores atómicos, promesas, futuros, cerraduras y variables de condición.
Vea las secciones en: std :: hilo
std::async
ejecuta la función dada, ya sea de manera asincrónica o perezosa, luego devuelve un std::future
que contiene el resultado de esa llamada de función.
El primer parámetro es la política que puede ser:
std::launch::async | std::launch::deferred
Depende de la implementación si realizar una ejecución asíncrona o una evaluación perezosa.std::launch::async
Ejecutar el objeto llamable en un nuevo hilo.std::launch::deferred
Realice una evaluación perezosa en el hilo actual. 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
and std::end
se agregaron funciones gratuitas para devolver y finalizar iteradores de un contenedor genéricamente. Estas funciones también funcionan con matrices sin procesar que no tienen funciones de miembros begin
y 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
Anthony Calandra
Ver: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
MIT