C++20 inclui os seguintes novos recursos de linguagem:
C++20 inclui os seguintes novos recursos de biblioteca:
C++17 inclui os seguintes novos recursos de linguagem:
C++17 inclui os seguintes novos recursos de biblioteca:
C++14 inclui os seguintes novos recursos de linguagem:
C++14 inclui os seguintes novos recursos de biblioteca:
C++ 11 inclui os seguintes novos recursos de linguagem:
C++11 inclui os seguintes novos recursos de biblioteca:
Observação: embora esses exemplos ilustrem como usar corrotinas em um nível básico, há muito mais coisas acontecendo quando o código é compilado. Esses exemplos não pretendem ser uma cobertura completa das corrotinas do C++20. Como as classes
generator
etask
ainda não são fornecidas pela biblioteca padrão, usei a biblioteca cppcoro para compilar esses exemplos.
As corrotinas são funções especiais que podem ter sua execução suspensa e retomada. Para definir uma corrotina, as palavras-chave co_return
, co_await
ou co_yield
devem estar presentes no corpo da função. As corrotinas do C++20 não têm pilha; a menos que seja otimizado pelo compilador, seu estado é alocado no heap.
Um exemplo de corrotina é uma função geradora , que produz (ou seja, gera) um valor a cada invocação:
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;
}
A função geradora range
acima gera valores começando no start
até end
(exclusivo), com cada etapa de iteração produzindo o valor atual armazenado em start
. O gerador mantém seu estado em cada invocação do range
(nesse caso, a invocação é para cada iteração no loop for). co_yield
pega a expressão dada, retorna (ou seja, retorna) seu valor e suspende a corrotina nesse ponto. Ao retomar, a execução continua após co_yield
.
Outro exemplo de corrotina é uma tarefa , que é uma computação assíncrona executada quando a tarefa é aguardada:
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;
}
Neste exemplo, a palavra-chave co_await
é introduzida. Esta palavra-chave pega uma expressão e suspende a execução se o que você está aguardando (neste caso, a leitura ou gravação) não estiver pronto, caso contrário você continua a execução. (Observe que nos bastidores, co_yield
usa co_await
.)
Usando uma tarefa para avaliar preguiçosamente um valor:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Os conceitos são chamados de predicados de tempo de compilação que restringem os tipos. Eles assumem o seguinte formato:
template < template-parameter-list >
concept concept-name = constraint-expression;
onde constraint-expression
é avaliada como um booleano constexpr. As restrições devem modelar requisitos semânticos, como se um tipo é numérico ou hashável. Um erro do compilador ocorre se um determinado tipo não satisfaz o conceito ao qual está vinculado (ou seja, constraint-expression
retorna false
). Como as restrições são avaliadas em tempo de compilação, elas podem fornecer mensagens de erro mais significativas e segurança em tempo de execução.
// `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>;
Há uma variedade de formas sintáticas para impor conceitos:
// 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> () {
// ...
};
A palavra-chave 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;
}
Observe que a lista de parâmetros em uma expressão requires
é opcional. Cada requisito em uma expressão requires
é um dos seguintes:
template < typename T>
concept callable = requires (T f) { f (); };
typename
seguida por um nome de tipo, afirma que o nome de tipo fornecido é 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
, especificam restrições adicionais (como aquelas em argumentos de parâmetros locais). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
Veja também: biblioteca de conceitos.
C++20 apresenta o operador de nave espacial ( <=>
) como uma nova maneira de escrever funções de comparação que reduzem o clichê e ajudam os desenvolvedores a definir uma semântica de comparação mais clara. Definir um operador de comparação de três vias gerará automaticamente as outras funções do operador de comparação (ou seja ==
, !=
, <
, etc.).
Três ordenações são introduzidas:
std::strong_ordering
: A ordem forte distingue entre itens iguais (idênticos e intercambiáveis). Fornece ordenação less
, greater
, equivalent
e equal
. Exemplos de comparações: busca por um valor específico em uma lista, valores de números inteiros, strings com distinção entre maiúsculas e minúsculas.std::weak_ordering
: A ordem fraca distingue entre itens equivalentes (não idênticos, mas podem ser intercambiáveis para fins de comparação). Fornece ordenação less
, greater
e equivalent
. Exemplos de comparações: strings que não diferenciam maiúsculas de minúsculas, classificação, comparação de alguns, mas não todos, membros visíveis de uma classe.std::partial_ordering
: A ordenação parcial segue o mesmo princípio da ordenação fraca, mas inclui o caso em que uma ordenação não é possível. Fornece ordenação less
, greater
, equivalent
e unordered
. Exemplos de comparações: valores de ponto flutuante (por exemplo, NaN
).Um operador de comparação de três vias padrão faz uma comparação entre membros:
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
Você também pode definir suas próprias comparações:
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
Sintaxe do inicializador designada no estilo C. Quaisquer campos de membro que não estejam explicitamente listados na lista de inicializadores designada são inicializados por padrão.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Use sintaxe de modelo familiar em expressões lambda.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Esse recurso simplifica padrões de código comuns, ajuda a manter os escopos restritos e oferece uma solução elegante para um problema comum de vida útil.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Fornece uma dica ao otimizador de que a instrução rotulada tem uma alta probabilidade de ser executada.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Se um dos atributos provável/improvável aparecer após o parêntese direito de uma instrução if, isso indica que é provável/improvável que a ramificação tenha sua subinstrução (corpo) executada.
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
Também pode ser aplicado à subinstrução (corpo) de uma instrução de iteração.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
Capturar this
implicitamente em uma captura lambda usando [=]
agora está obsoleto; prefira capturar explicitamente usando [=, this]
ou [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
As classes agora podem ser usadas em parâmetros de modelo sem tipo. Os objetos passados como argumentos de modelo têm o tipo const T
, onde T
é o tipo do objeto e tem duração de armazenamento 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 }>();
Funções virtuais agora podem ser constexpr
e avaliadas em tempo de compilação. funções virtuais constexpr
podem substituir funções virtuais não constexpr
e vice-versa.
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
Selecione condicionalmente em tempo de compilação se um construtor é explicitado ou não. explicit(true)
é o mesmo 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
Semelhante às funções constexpr
, mas as funções com um especificador consteval
devem produzir uma constante. Estas são chamadas de 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
Coloque os membros de uma enum no escopo para melhorar a legibilidade. 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 " ;
}
}
Depois:
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 pacotes de parâmetros por valor:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Capture pacotes de parâmetros por referência:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Fornece um tipo padrão para representar strings UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
O especificador constinit
requer que uma variável seja inicializada em tempo de compilação.
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.
Ajuda a suportar macros variáveis avaliando o argumento fornecido se a macro variável não estiver vazia.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
Os conceitos também são fornecidos pela biblioteca padrão para a construção de conceitos mais complicados. Alguns deles incluem:
Conceitos básicos da linguagem:
same_as
- especifica que dois tipos são iguais.derived_from
- especifica que um tipo é derivado de outro tipo.convertible_to
- especifica que um tipo é implicitamente conversível em outro tipo.common_with
- especifica que dois tipos compartilham um tipo comum.integral
- especifica que um tipo é um tipo integral.default_constructible
- especifica que um objeto de um tipo pode ser construído por padrão.Conceitos de comparação:
boolean
- especifica que um tipo pode ser usado em contextos booleanos.equality_comparable
- especifica que operator==
é uma relação de equivalência.Conceitos de objeto:
movable
- especifica que um objeto de um tipo pode ser movido e trocado.copyable
- especifica que um objeto de um tipo pode ser copiado, movido e trocado.semiregular
- especifica que um objeto de um tipo pode ser copiado, movido, trocado e construído por padrão.regular
- especifica que um tipo é regular , ou seja, é semiregular
e equality_comparable
.Conceitos chamáveis:
invocable
- especifica que um tipo que pode ser chamado pode ser invocado com um determinado conjunto de tipos de argumentos.predicate
- especifica que um tipo que pode ser chamado é um predicado booleano.Veja também: conceitos.
Combine a simplicidade do printf
com a segurança de tipo do iostream
. Usa colchetes como espaços reservados e suporta formatação personalizada semelhante aos 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 formatar 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"
Armazena operações de saída para o fluxo de saída encapsulado, garantindo a sincronização (ou seja, sem intercalação de saída).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Um span é uma visão (isto é, sem propriedade) de um contêiner que fornece acesso com verificação de limites a um grupo contíguo de elementos. Como as visualizações não possuem seus elementos, elas são baratas para construir e copiar - uma maneira simplificada de pensar sobre as visualizações é que elas mantêm referências aos seus dados. Ao contrário de manter um ponteiro/iterador e um campo de comprimento, um span agrupa ambos em um único objeto.
Os spans podem ser dimensionados dinamicamente ou fixos (conhecidos como extensão ). Spans de tamanho fixo se beneficiam da verificação de limites.
Span não propaga const, portanto, para construir um span somente leitura, use std::span<const T>
.
Exemplo: usando um intervalo de tamanho dinâmico para imprimir números inteiros de vários contêineres.
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.
Exemplo: um intervalo de tamanho estático não será compilado para contêineres que não correspondam à extensão do 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 fornece um novo cabeçalho <bit>
que fornece algumas operações de bits, incluindo popcount.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Constantes matemáticas incluindo PI, número de Euler, etc. definidas no cabeçalho <numbers>
.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Função de predicado que é verdadeira quando é chamada em um contexto de tempo de compilação.
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]`
Strings (e visualizações de strings) agora têm as funções de membro starts_with
e ends_with
para verificar se uma string começa ou termina com a string fornecida.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Contêineres associativos, como conjuntos e mapas, possuem uma função de membro contains
, que pode ser usada em vez do idioma "localizar e verificar o final do 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
Uma maneira mais segura de reinterpretar um objeto de um tipo para outro.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Calcule o ponto médio de dois inteiros com segurança (sem overflow).
std::midpoint ( 1 , 3 ); // == 2
Converte o objeto array/"semelhante a array" fornecido em um 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 os primeiros N argumentos (onde N é o número de argumentos após a função fornecida a std::bind_front
) a uma determinada função livre, lambda ou função membro.
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
Fornece std::erase
e/ou std::erase_if
para uma variedade de contêineres STL, como string, lista, vetor, mapa, etc.
Para apagar por valor, use std::erase
, ou para especificar um predicado quando apagar elementos, use std::erase_if
. Ambas as funções retornam o número de elementos apagados.
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}
Funções auxiliares para dar nomes aos resultados de comparação:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
Veja também: comparação de três vias.
Compara lexicograficamente dois intervalos usando comparação de três vias e produz um resultado do tipo de categoria de comparação aplicável mais forte.
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
Veja também: comparação de três vias, auxiliares de comparação de três vias.
Dedução automática de argumentos de modelo, muito parecida com a feita para funções, mas agora incluindo construtores de classe.
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>
Seguindo as regras de dedução de auto
, respeitando a lista de parâmetros de modelo não-tipo de tipos permitidos[*], os argumentos do modelo podem ser deduzidos dos tipos de seus 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 exemplo, você não pode usar double
como tipo de parâmetro de modelo, o que também torna essa dedução inválida usando auto
.
Uma expressão fold executa uma dobra de um pacote de parâmetros de modelo sobre um operador binário.
(... op e)
ou (e op ...)
, onde op
é um operador de dobra e e
é um pacote de parâmetros não expandido, são chamadas dobras unárias .(e1 op ... op e2)
, onde op
são operadores de dobra, é chamada de dobra binária . e1
ou e2
é um pacote de parâmetros não expandido, mas não 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
Alterações na dedução auto
quando usada com a sintaxe de inicialização uniforme. Anteriormente, auto x {3};
deduz um std::initializer_list<int>
, que agora deduz para 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 em tempo de compilação 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 Capturar this
em um ambiente lambda era anteriormente apenas para referência. Um exemplo de onde isso é problemático é o código assíncrono que usa retornos de chamada que exigem que um objeto esteja disponível, potencialmente após seu tempo de vida. *this
(C++17) agora fará uma cópia do objeto atual, enquanto this
(C++11) continua a capturar por referência.
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
O especificador embutido pode ser aplicado tanto a variáveis quanto a funções. Uma variável declarada inline tem a mesma semântica que uma função declarada 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
Também pode ser usado para declarar e definir uma variável de membro estático, de forma que não precise ser inicializada no arquivo de origem.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Usando o operador de resolução de namespace para criar definições de namespace aninhadas.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
O código acima pode ser escrito assim:
namespace A ::B::C {
int i;
}
Uma proposta de desestruturação da inicialização, que permitiria escrever auto [ x, y, z ] = expr;
onde o tipo de expr
era um objeto semelhante a uma tupla, cujos elementos seriam vinculados às variáveis x
, y
e z
(que esta construção declara). Objetos semelhantes a tupla incluem std::tuple
, std::pair
, std::array
e estruturas 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
}
Novas versões das instruções if
e switch
que simplificam os padrões de código comuns e ajudam os usuários a manter os escopos restritos.
{
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 ());
}
Escreva o código que é instanciado dependendo de uma condição de tempo de compilação.
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);
Um literal de caractere que começa com u8
é um literal de caractere do tipo char
. O valor de um literal de caractere UTF-8 é igual ao valor do ponto de código ISO 10646.
char x = u8 ' x ' ;
Enums agora podem ser inicializados usando sintaxe entre chaves.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++ 17 introduz três novos atributos: [[fallthrough]]
, [[nodiscard]]
e [[maybe_unused]]
.
[[fallthrough]]
indica ao compilador que a falha em uma instrução switch é o comportamento pretendido. Este atributo só pode ser usado em uma instrução switch e deve ser colocado antes do próximo rótulo case/default. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
emite um aviso quando uma função ou classe possui este atributo e seu valor de retorno é descartado. [[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 ao compilador que uma variável ou parâmetro pode não ser utilizado e é intencional. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
O operador __has_include (operand)
pode ser usado nas expressões #if
e #elif
para verificar se um cabeçalho ou arquivo de origem ( operand
) está disponível para inclusão ou não.
Um caso de uso disso seria usar duas bibliotecas que funcionam da mesma maneira, usando a de backup/experimental caso a preferida não seja encontrada no 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
Também pode ser usado para incluir cabeçalhos existentes sob diferentes nomes ou locais em várias plataformas, sem saber em qual plataforma o programa está sendo executado. Os cabeçalhos OpenGL são um bom exemplo disso, localizados no diretório OpenGL
no macOS e GL
em outros 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
A dedução de argumentos de modelo de classe (CTAD) permite que o compilador deduza argumentos de modelo a partir de argumentos do construtor.
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 tipos definidos pelo usuário, guias de dedução podem ser usados para orientar o compilador sobre como deduzir argumentos de modelo, se aplicável:
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
O modelo de classe std::variant
representa uma union
de tipo seguro. Uma instância de std::variant
em qualquer momento contém um valor de um de seus tipos alternativos (também é possível que não tenha 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
O modelo de classe std::optional
gerencia um valor opcional contido, ou seja, um valor que pode ou não estar presente. Um caso de uso comum para opcional é o valor de retorno de uma função que pode falhar.
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 )) {
// ...
}
Um contêiner de tipo seguro para valores únicos de qualquer 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
Uma referência não proprietária a uma string. Útil para fornecer uma abstração sobre strings (por exemplo, para análise).
// 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"
Invoque um objeto Callable
com parâmetros. Exemplos de objetos que podem ser chamados são std::function
ou lambdas; objetos que podem ser chamados de forma semelhante a uma função regular.
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
Invoque um objeto Callable
com uma tupla de argumentos.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
A nova biblioteca std::filesystem
fornece uma maneira padrão de manipular arquivos, diretórios e caminhos em um sistema de arquivos.
Aqui, um arquivo grande é copiado para um caminho temporário se houver espaço disponível:
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 " ));
}
}
O novo tipo std::byte
fornece uma maneira padrão de representar dados como um byte. Os benefícios de usar std::byte
sobre char
ou unsigned char
é que ele não é um tipo de caractere e também não é um tipo aritmético; enquanto as únicas sobrecargas de operador disponíveis são operações 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
Observe que std::byte
é simplesmente um enum, e a inicialização reforçada de enums torna-se possível graças à inicialização de lista direta de enums.
Mover nós e mesclar contêineres sem a sobrecarga de cópias, movimentações ou alocações/desalocações de heap dispendiosas.
Movendo elementos de um mapa para outro:
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" } };
Inserindo um conjunto inteiro:
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 }
Inserindo elementos que sobrevivem ao contêiner:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Alterando a chave de um elemento do 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" } }
Muitos dos algoritmos STL, como os métodos copy
, find
e sort
, passaram a suportar as políticas de execução paralela : seq
, par
e par_unseq
que se traduzem em "sequencialmente", "paralelo" e "paralelo não sequenciado".
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));
Amostra n elementos na sequência fornecida (sem substituição), onde cada elemento tem chances iguais de ser selecionado.
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
Fixe o valor fornecido entre um limite inferior e superior.
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
Dobre um determinado intervalo de elementos. Conceitualmente semelhante a std::accumulate
, mas std::reduce
realizará a dobra em paralelo. Devido à dobra ser feita em paralelo, se você especificar uma operação binária, ela deverá ser associativa e comutativa. Uma determinada operação binária também não deve alterar nenhum elemento ou invalidar nenhum iterador dentro do intervalo determinado.
A operação binária padrão é std::plus com um 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
Além disso, você pode especificar transformações para redutores:
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
Suporte para somas de prefixos (varreduras inclusivas e exclusivas) junto com transformações.
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 divisor comum (GCD) e mínimo múltiplo comum (LCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Função de utilidade que retorna a negação do resultado da função 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
Converta integrais e flutuantes em uma string ou vice-versa. As conversões não são lançadas, não são alocadas e são mais seguras do que os equivalentes da biblioteca padrão C.
Os usuários são responsáveis por alocar armazenamento suficiente necessário para std::to_chars
, ou a função falhará ao definir o objeto de código de erro em seu valor de retorno.
Essas funções permitem que você passe opcionalmente uma base (o padrão é base 10) ou um especificador de formato para entrada de tipo flutuante.
std::to_chars
retorna um ponteiro char (não const) que é um após o final da string que a função gravou dentro do buffer fornecido e um objeto de código de erro.std::from_chars
retorna um ponteiro const char que em caso de sucesso é igual ao ponteiro final passado para a função e um objeto de código de erro.Ambos os objetos de código de erro retornados por essas funções são iguais ao objeto de código de erro inicializado por padrão em caso de sucesso.
Converta o número 123
em 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 */ }
Converta de um std::string
com valor "123"
em um número inteiro:
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 */ }
Fornece funções auxiliares de abs, round, ceil e floor para std::chrono::duration
e 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
Literais binários fornecem uma maneira conveniente de representar um número de base 2. É possível separar dígitos com '
.
0b110 // == 6
0b1111'1111 // == 255
C++ 14 agora permite o especificador auto
de tipo na lista de parâmetros, habilitando lambdas polimórficas.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Isso permite criar capturas lambda inicializadas com expressões arbitrárias. O nome dado ao valor capturado não precisa estar relacionado a nenhuma variável nos escopos anexos e introduz um novo nome dentro do corpo lambda. A expressão de inicialização é avaliada quando o lambda é criado (não quando é invocado ).
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
Como agora é possível mover (ou avançar ) valores para um lambda que anteriormente só podiam ser capturados por cópia ou referência, agora podemos capturar tipos somente de movimentação em um lambda por valor. Observe que no exemplo abaixo o p
na lista de captura de task2
no lado esquerdo de =
é uma nova variável privada para o corpo lambda e não se refere ao original 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
O uso dessas capturas de referência pode ter nomes diferentes da variável referenciada.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
Usando um tipo de retorno auto
em C++14, o compilador tentará deduzir o tipo para você. Com lambdas, agora você pode deduzir seu tipo de retorno usando auto
, o que torna possível retornar uma referência deduzida ou uma referência de valor.
// 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`
O especificador de tipo decltype(auto)
também deduz um tipo como auto
faz. No entanto, ele deduz tipos de retorno enquanto mantém suas referências e qualificadores cv, enquanto auto
não o faz.
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);
Veja também: decltype (C++11)
.
No C++ 11, os corpos da função constexpr
só podiam conter um conjunto muito limitado de sintaxes, incluindo (mas não limitado a): typedef
s, using
s e uma única instrução return
. No C++14, o conjunto de sintaxes permitidas se expande bastante para incluir a sintaxe mais comum, como instruções if
, múltiplos return
s, loops, etc.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++ 14 permite que variáveis sejam modeladas:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++ 14 introduz o atributo [[deprecated]]
para indicar que uma unidade (função, classe, etc.) é desencorajada e provavelmente gera avisos de compilação. Se for fornecido um motivo, ele será incluído nos avisos.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Novos literais definidos pelo usuário para tipos de biblioteca padrão, incluindo novos literais integrados para chrono
e basic_string
. Eles podem ser constexpr
, o que significa que podem ser usados em tempo de compilação. Alguns usos para esses literais incluem análise de inteiros em tempo de compilação, literais binários e literais de números imaginários.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
O modelo de classe std::integer_sequence
representa uma sequência de inteiros em tempo de compilação. Existem alguns ajudantes criados em cima:
std::make_integer_sequence<T, N>
- cria uma sequência de 0, ..., N - 1
com tipo T
.std::index_sequence_for<T...>
- converte um pacote de parâmetros de modelo em uma sequência inteira.Converta um array em uma 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
é a maneira recomendada de criar instâncias de std::unique_ptr
s devido aos seguintes motivos:
new
operador.foo
assim: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
O compilador é livre para chamar new T{}
, então function_that_throws()
, e assim por diante... Como alocamos dados no heap na primeira construção de um T
, introduzimos um vazamento aqui. Com std::make_unique
, recebemos segurança de exceção:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Consulte a seção sobre ponteiros inteligentes (C++11) para obter mais informações sobre std::unique_ptr
e std::shared_ptr
.
Mover um objeto significa transferir a propriedade de algum recurso que ele gerencia para outro objeto.
O primeiro benefício da semântica de movimentação é a otimização do desempenho. Quando um objeto está prestes a chegar ao fim de sua vida útil, seja porque é temporário ou por chamar explicitamente std::move
, uma movimentação geralmente é uma maneira mais barata de transferir recursos. Por exemplo, mover um std::vector
é apenas copiar alguns ponteiros e estados internos para o novo vetor - a cópia envolveria ter que copiar cada elemento contido no vetor, o que é caro e desnecessário se o vetor antigo for em breve destruído.
As movimentações também possibilitam que tipos não copiáveis, como std::unique_ptr
s (ponteiros inteligentes), garantam no nível da linguagem que haja apenas uma instância de um recurso sendo gerenciado por vez, enquanto é possível transferir uma instância entre escopos.
Consulte as seções sobre: referências de rvalue, funções de membro especiais para semântica de movimentação, std::move
, std::forward
, forwarding references
.
C++ 11 introduz uma nova referência denominada rvalue reference . Uma referência rvalue a T
, que é um parâmetro de tipo não modelo (como int
ou um tipo definido pelo usuário), é criada com a sintaxe T&&
. As referências de Rvalue vinculam-se apenas a rvalues.
Digite dedução com lvalues e 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)
Veja também: std::move
, std::forward
, forwarding references
.
Também conhecidas (não oficialmente) como referências universais . Uma referência de encaminhamento é criada com a sintaxe T&&
onde T
é um parâmetro de tipo de modelo ou usando auto&&
. Isso permite o encaminhamento perfeito : a capacidade de passar argumentos enquanto mantém sua categoria de valor (por exemplo, lvalues permanecem como lvalues, temporários são encaminhados como rvalues).
As referências de encaminhamento permitem que uma referência seja vinculada a um lvalue ou rvalue dependendo do tipo. As referências de encaminhamento seguem as regras de colapso de referência :
T& &
se torna T&
T& &&
torna-se T&
T&& &
se torna T&
T&& &&
torna-se T&&
dedução de tipo auto
com lvalues e rvalues:
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`
Dedução de parâmetro de tipo de modelo com lvalues e rvalues:
// 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&&)
Veja também: std::move
, std::forward
, rvalue references
.
A sintaxe ...
cria um pacote de parâmetros ou expande um. Um pacote de parâmetros de modelo é um parâmetro de modelo que aceita zero ou mais argumentos de modelo (não tipos, tipos ou modelos). Um modelo com pelo menos um pacote de parâmetros é chamado de modelo variado .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Um uso interessante para isso é criar uma lista de inicializadores a partir de um pacote de parâmetros para iterar sobre argumentos de funções variáveis.
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
Um contêiner leve de elementos, semelhante a uma matriz, criado usando uma sintaxe de "lista entre chaves". Por exemplo, { 1, 2, 3 }
cria uma sequência de números inteiros, que possui o tipo std::initializer_list<int>
. Útil como substituto da passagem de um vetor de objetos para uma função.
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
Asserções que são avaliadas em tempo de compilação.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
variáveis digitadas auto
são deduzidas pelo compilador de acordo com o tipo de seu 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
Extremamente útil para facilitar a leitura, especialmente para tipos complicados:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
As funções também podem deduzir o tipo de retorno usando auto
. Em C++ 11, um tipo de retorno deve ser especificado explicitamente ou usando decltype
da seguinte forma:
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
O tipo de retorno final no exemplo acima é o tipo declarado (consulte a seção sobre decltype
) da expressão x + y
. Por exemplo, se x
for um número inteiro e y
for um duplo, decltype(x + y)
será um duplo. Portanto, a função acima deduzirá o tipo dependendo do tipo que a expressão x + y
produz. Observe que o tipo de retorno final tem acesso aos seus parâmetros, e this
quando apropriado.
Um lambda
é um objeto de função sem nome capaz de capturar variáveis no escopo. Possui: uma lista de captura ; um conjunto opcional de parâmetros com um tipo de retorno opcional; e um corpo. Exemplos de listas de captura:
[]
- não captura nada.[=]
- captura objetos locais (variáveis locais, parâmetros) no escopo por valor.[&]
- captura objetos locais (variáveis locais, parâmetros) no escopo por referência.[this]
- capture this
por referência.[a, &b]
- captura objetos a
por valor, b
por referência. 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`
Por padrão, as capturas de valor não podem ser modificadas dentro do lambda porque o método gerado pelo compilador é marcado como const
. A palavra-chave mutable
permite modificar variáveis capturadas. A palavra-chave é colocada após a lista de parâmetros (que deve estar presente mesmo que esteja vazia).
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
é um operador que retorna o tipo declarado de uma expressão passada para ele. Os qualificadores cv e as referências são mantidos se fizerem parte da expressão. Exemplos 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`
Veja também: decltype(auto) (C++14)
.
Semanticamente semelhante ao uso de um typedef
, entretanto, os aliases de tipo com using
são mais fáceis de ler e são compatíveis com modelos.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++ 11 introduz um novo tipo de ponteiro nulo projetado para substituir a macro NULL
do C. O próprio nullptr
é do tipo std::nullptr_t
e pode ser convertido implicitamente em tipos de ponteiro e, ao contrário de NULL
, não é conversível em tipos integrais, exceto bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Enums de tipo seguro que resolvem uma variedade de problemas com enums de estilo C, incluindo: conversões implícitas, incapacidade de especificar o tipo subjacente, poluição de escopo.
// 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;
Os atributos fornecem uma sintaxe universal sobre __attribute__(...)
, __declspec
, etc.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Expressões constantes são expressões que são possivelmente avaliadas pelo compilador em tempo de compilação. Somente cálculos não complexos podem ser realizados em uma expressão constante (essas regras são progressivamente relaxadas em versões posteriores). Use o especificador constexpr
para indicar a variável, a função etc. é uma expressão 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
No snippet anterior, observe que o cálculo ao chamar square
é realizado em tempo de compilação e, em seguida, o resultado é incorporado na geração de código, enquanto square2
é chamado no tempo de execução.
Os valores constexpr
são aqueles que o compilador pode avaliar, mas não são garantidos em tempo de compilação:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Expressões constantes com classes:
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 );
Os construtores agora podem chamar outros construtores na mesma classe usando uma lista de inicializador.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Os literais definidos pelo usuário permitem estender o idioma e adicionar sua própria sintaxe. Para criar uma função literal, defina um T operator "" X(...) { ... }
que retorna um tipo T
, com um nome X
. Observe que o nome desta função define o nome do literal. Quaisquer nomes literais que não começam com um sublinhado são reservados e não serão invocados. Existem regras sobre quais parâmetros uma função literal definida pelo usuário deve aceitar, de acordo com que tipo o literal é chamado.
Convertendo Celsius em 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
String para conversão inteira:
// `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 uma função virtual substitui outra função virtual. Se a função virtual não substituir a função virtual de um pai, lança um erro do 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 uma função virtual não pode ser substituída em uma classe derivada ou que uma classe não pode ser herdada.
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
};
A classe não pode ser herdada de.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Uma maneira mais elegante e eficiente de fornecer uma implementação padrão de uma função, como um construtor.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
Com herança:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Uma maneira mais elegante e eficiente de fornecer uma implementação excluída de uma função. Útil para prevenir cópias em 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
Açúcar sintático para iterar sobre os elementos de um contêiner.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Observe a diferença ao usar int
em vez de int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
O construtor de cópias e o operador de atribuição de cópias são chamados quando cópias são feitas e, com a introdução da semântica de movimentos do C ++ 11, agora existe um construtor de movimentos e operador de atribuição de movimentos para movimentos.
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
Os construtores de conversão converterão valores da sintaxe da lista apoiada em argumentos do construtor.
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)
Observe que a sintaxe da lista apoiada não permite estreitar:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Observe que, se um construtor aceitar um std::initializer_list
, ele será chamado:
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>)
As funções de conversão agora podem ser explicitadas usando o 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 os membros de um espaço para nome em linha são tratados como se fizessem parte do espaço para nome dos pais, permitindo a especialização das funções e facilitando o processo de versão. Esta é uma propriedade transitiva, se a contém B, que, por sua vez
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 os membros não estáticos de dados sejam inicializados onde são declarados, potencialmente limpando os construtores de inicializações padrão.
// 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 };
};
O C ++ 11 agora pode inferir quando uma série de suportes para ângulo reto é usado como operador ou como uma declaração de fechamento do typedef, sem precisar adicionar espaço em branco.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
As funções do membro agora podem ser qualificadas, dependendo de *this
é uma referência de LValue ou 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&`
O C ++ 11 permite funções e lambdas uma sintaxe alternativa para especificar seus tipos de retorno.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Esse recurso é especialmente útil quando certos tipos de retorno não podem ser resolvidos:
// 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;
}
Em C ++ 14, decltype(auto) (C++14)
pode ser usado.
O especificador noexcept
especifica se uma função pode lançar exceções. É uma versão aprimorada 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
As funções que não arremessam podem chamar funções potencialmente jogando. Sempre que uma exceção é lançada e a busca por um manipulador encontra o bloco mais externo de uma função que não atinge, a função std :: termine é chamada.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Fornece tipos padrão para representar strings UTF-8.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
O C ++ 11 apresenta uma nova maneira de declarar literais de cordas como "literais de cordas cruas". Os caracteres emitidos a partir de uma sequência de escape (guias, feeds de linha, barrafas únicas, etc.) podem ser inseridas cruas ao preservar a formatação. Isso é útil, por exemplo, escrever texto literário, que pode conter muitas citações ou formatação especial. Isso pode facilitar a leitura e a manutenção de suas cordas.
Uma corda crua literal é declarada usando a seguinte sintaxe:
R"delimiter(raw_characters)delimiter"
onde:
delimiter
é uma sequência opcional de caracteres feitos de qualquer caractere de origem, exceto parênteses, barracas e espaços.raw_characters
é qualquer sequência de caracteres bruta; não deve conter a sequência de fechamento ")delimiter"
.Exemplo:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
indica que o objeto passado para ele pode ter seus recursos transferidos. O uso de objetos que foram movidos deve ser usado com cuidado, pois podem ser deixados em um estado não especificado (veja: o que posso fazer com um objeto movido?).
Uma definição de std::move
(executar um movimento nada mais é do que lançar para uma referência de rvalue):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Transferindo 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`
Retorna os argumentos transmitidos a ele, mantendo sua categoria de valor e qualificadores de CV. Útil para código genérico e fábricas. Usado em conjunto com forwarding references
.
Uma definição de std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Um exemplo de um wrapper
de função que apenas encaminha outros A
para um novo construtor de cópia ou movimentação 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
Veja também: forwarding references
, rvalue references
.
A biblioteca std::thread
fornece uma maneira padrão de controlar os threads, como desova e matar. No exemplo abaixo, vários threads são gerados para fazer cálculos diferentes e, em seguida, o programa espera que todos eles terminem.
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
}
Converte um argumento numérico em uma std::string
.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Traços de tipo define uma interface baseada em modelo de tempo de compilação para consultar ou modificar as propriedades dos 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);
O C ++ 11 apresenta novos ponteiros inteligentes: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
agora fica preguiçoso e depois removido em C ++ 17.
std::unique_ptr
é um ponteiro móvel não copiável que gerencia sua própria memória alocada por heap. NOTA: Prefira o uso do std::make_X
Helper Functions em vez de usar construtores. Veja as seções para std :: make_unique e 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
Um std::shared_ptr
é um ponteiro inteligente que gerencia um recurso compartilhado em vários proprietários. Um ponteiro compartilhado contém um bloco de controle que possui alguns componentes, como o objeto gerenciado e um contador de referência. Todo o acesso ao bloco de controle é seguro para roscas, no entanto, manipular o próprio objeto gerenciado não é seguro para encadeamento.
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);
A biblioteca Chrono contém um conjunto de funções e tipos de utilidade que lidam com durações , relógios e momentos . Um caso de uso desta biblioteca é o código de benchmarking:
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`
As tuplas são uma coleção de tamanho fixo de valores heterogêneos. Acesse os elementos de uma std::tuple
descompactando usando std::tie
ou 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"
Cria uma tupla de referências de LValue. Útil para descompactar os objetos std::pair
e std::tuple
. Use std::ignore
como um espaço reservado para obter valores ignorados. Em C ++ 17, as ligações estruturadas devem ser usadas.
// 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
é um recipiente construído sobre uma matriz no estilo C. Suporta operações comuns de contêiner, como classificação.
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 }
Esses contêineres mantêm a complexidade média de tempo constante para pesquisa, inserção e remoção de operações. Para alcançar a complexidade do tempo constante, os sacrifícios ordenam a velocidade com elementos de hash em baldes. Existem quatro recipientes não ordenados:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
é a maneira recomendada de criar instâncias de std::shared_ptr
s devido aos seguintes motivos:
new
operador.foo
assim: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
O compilador está livre para ligar para new T{}
, depois function_that_throws()
e assim por diante ... Como alocamos dados sobre a pilha na primeira construção de um T
, introduzimos um vazamento aqui. Com std::make_shared
, recebemos a segurança de exceções:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
, precisamos alocar memória para T
; então, no ponteiro compartilhado, precisamos alocar memória para o bloco de controle dentro do ponteiro. Consulte a seção sobre indicações inteligentes para obter mais informações sobre std::unique_ptr
e std::shared_ptr
.
std::ref(val)
é usado para criar objeto do tipo std::reference_wrapper
que mantém a referência do val. Usado nos casos em que a passagem de referência usual usando &
não compila OR &
é descartada devido à dedução do tipo. std::cref
é semelhante, mas criado, o Wrapper de referência possui uma referência 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
O C ++ 11 apresenta um modelo de memória para C ++, o que significa suporte da biblioteca para operações de rosqueamento e atômico. Algumas dessas operações incluem (mas não se limitam a) cargas/lojas atômicas, comparação e troca, bandeiras atômicas, promessas, futuros, bloqueios e variáveis de condição.
Veja as seções em: std :: thread
std::async
executa a função fornecida de forma assíncrona ou preguiçosamente avaliada e, em seguida, retorna um std::future
que mantém o resultado dessa chamada de função.
O primeiro parâmetro é a política que pode ser:
std::launch::async | std::launch::deferred
está de acordo com a implementação executar execução assíncrona ou avaliação preguiçosa.std::launch::async
Run O objeto chamável em um novo thread.std::launch::deferred
execute uma avaliação preguiçosa no thread atual. 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
e std::end
foram adicionadas para retornar os iteradores de início e final de um contêiner genericamente. Essas funções também funcionam com matrizes brutas que não possuem funções begin
e 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
Veja: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributores
MIT