O autor do material, cuja tradução publicamos hoje, diz que o C ++, em sua forma moderna, quando comparado ao que era esse idioma há vários anos, mudou significativamente para melhor. Obviamente, essas mudanças não aconteceram imediatamente. Por exemplo, antigamente, o C ++ não possuía dinamismo. Não foi fácil encontrar uma pessoa que pudesse dizer que tem sentimentos ternos por esse idioma. Tudo mudou quando os responsáveis pela padronização da linguagem decidiram dar lugar a inovações. Em 2011, o C ++ se tornou uma linguagem dinâmica, uma linguagem em constante evolução e que causa emoções muito mais positivas aos programadores.
Não pense que o idioma se tornou mais fácil. Ainda pode ser chamada de uma das linguagens de programação mais complexas amplamente usadas, se não a mais complexa. Mas o C ++ moderno se tornou muito mais amigável do que antes.

Hoje falaremos sobre alguns dos novos recursos da linguagem (a partir do C ++ 11, que, a propósito, já tem 8 anos), que serão úteis para qualquer programador.
Palavra-chave automática
Desde que a palavra
auto
chave
auto
apareceu no C ++ 11, a vida dos programadores se tornou mais fácil. Graças a essa palavra-chave, o compilador pode gerar tipos de variáveis em tempo de compilação, o que nos impede de sempre precisar especificar os tipos. Isso acabou sendo muito conveniente, por exemplo, nos casos em que você precisa trabalhar com tipos de dados como
map<string,vector<pair<int,int>>>
. Ao usar a palavra
auto
chave
auto
, há algumas coisas a considerar. Considere um exemplo:
auto an_int = 26;
Preste atenção à última linha deste exemplo, cujo comentário está marcado como
#1
(a seguir, de maneira semelhante, marcaremos as linhas que analisaremos após os exemplos). Não há inicializador nesta linha, você não pode fazer isso. O código localizado nesta linha impede que o compilador saiba qual deve ser o tipo da variável correspondente.
Inicialmente, a palavra
auto
chave
auto
em C ++ era bastante limitada. Em versões mais recentes do idioma, recursos adicionados
auto
. Aqui está outro exemplo:
auto merge(auto a, auto b)
As linhas
#1
e
#2
aplicam a inicialização variável usando chaves - outro novo recurso no C ++ 11.
Lembre-se de que, ao usar a palavra
auto
chave
auto
, o compilador deve ter uma maneira de inferir o tipo da variável.
Agora, uma pergunta interessante. O que acontece se você usar um design como
auto a = {1, 2, 3}
? O que é isso Vector ou causa de erro de compilação?
De fato, uma construção do formulário
std::initializer_list<type>
apareceu no C ++ 11. A lista entre parênteses de valores de inicialização será tratada como um contêiner usando a palavra
auto
chave
auto
.
E, finalmente, como já mencionado, a inferência de tipo pelo compilador pode ser extremamente útil se você precisar trabalhar com estruturas de dados complexas. Aqui está um exemplo:
void populate(auto &data) {
Dê uma olhada na linha
#1
. A expressão
auto [v1,v2] = itr.second
representa um novo recurso do C ++ 17. Essa é a chamada decomposição ao declarar variáveis. Nas versões anteriores do idioma, cada valor tinha que ser extraído individualmente. Graças a esse mecanismo, executar essas operações se tornou muito mais conveniente.
Além disso, se você precisar trabalhar com dados usando links, basta adicionar apenas um caractere a essa construção, convertendo-o no seguinte formato:
auto &[v1,v2] = itr.second
.
Expressões lambda
O C ++ 11 apresenta suporte para expressões lambda. Eles se parecem com funções anônimas em JavaScript, podem ser comparados com objetos funcionais sem nomes. Eles capturam variáveis em vários escopos, dependendo de sua descrição, para as quais construções sintáticas compactas são usadas. Além disso, eles podem ser atribuídos a variáveis.
As expressões Lambda são uma ferramenta muito útil para os casos em que você precisa executar uma pequena operação no código, mas não deseja gravar uma função separada para isso. Outro exemplo comum de seu uso é a criação de funções usadas na comparação de valores. Por exemplo:
std::vector<std::pair<int,int>> data = {{1,3}, {7,6}, {12, 4}};
Você pode encontrar muitas coisas interessantes neste pequeno exemplo.
Primeiro, preste atenção à conveniência de usar a inicialização variável usando chaves. A seguir, podemos ver as construções padrão
begin()
e
end()
, que também apareceram no C ++ 11. Em seguida, vem a função lambda, que é usada como um mecanismo para comparar dados. Os parâmetros desta função são declarados usando a palavra
auto
chave
auto
, esse recurso apareceu no C ++ 14. Anteriormente, essa palavra-chave não podia ser usada para descrever os parâmetros das funções.
Agora observe que a expressão lambda começa com colchetes -
[]
. Essa é a chamada máscara de variáveis. Ele determina o escopo da expressão, ou seja, permite controlar o relacionamento da expressão lambda com variáveis e objetos locais.
Aqui está um trecho
deste repositório dedicado aos recursos modernos do C ++:
[]
- a expressão não captura nada. Isso significa que em uma expressão lambda é impossível usar variáveis locais do escopo externo a ela. Somente parâmetros podem ser usados na expressão.[=]
- a expressão captura os valores de objetos locais (ou seja, variáveis locais, parâmetros). Isso significa que eles podem ser usados, mas não modificados.[&]
- a expressão captura referências a objetos locais. Eles podem ser modificados, conforme mostrado no exemplo a seguir.[this]
- a expressão captura o valor this
ponteiro.[a, &b]
- a expressão captura o valor do objeto a
e uma referência ao objeto b
.
Como resultado, se dentro da função lambda você precisar converter os dados para outro formato, poderá usar os mecanismos acima. Considere um exemplo:
std::vector<int> data = {2, 4, 4, 1, 1, 3, 9}; int factor = 7; for_each(begin(data), end(data), [&factor](int &val) {
Aqui, se a variável
factor
fosse acessada por valor (a variável mask
[factor]
seria usada para descrever a expressão lambda), na linha
#1
o valor do
factor
não poderia ser alterado - simplesmente porque não teríamos direito a executando tal operação. Neste exemplo, temos o direito de tais ações. Em tais situações, é importante não abusar dos recursos que acessam variáveis fornecidas por referência.
Além disso, observe que
val
também
val
acessado por referência. Isso garante que as alterações de dados que ocorrem na função lambda afetem o
vector
.
Expressões de inicialização variáveis dentro das construções if e switch
Eu realmente gostei dessa inovação do C ++ 17 logo depois que descobri. Considere um exemplo:
std::set<int> input = {1, 5, 3, 6}; if(auto it = input.find(7); it==input.end()){
Acontece que agora você pode inicializar as variáveis e comparar com o uso delas em um bloco
if
ou
switch
. Isso ajuda a escrever um código preciso. Aqui está uma descrição esquemática da estrutura em consideração:
if( init-statement(x); condition(x)) {
Executando cálculos em tempo de compilação usando constexpr
A
constexpr
nos oferece grandes oportunidades. Suponha que tenhamos algum tipo de expressão que precisa ser calculada, enquanto seu valor, após inicializá-lo com a variável correspondente, não será alterado. Essa expressão pode ser calculada antecipadamente e usada como macro. Ou, o que se tornou possível no C ++ 11, use a
constexpr
.
Os programadores se esforçam para minimizar a quantidade de computação realizada durante a execução do programa. Como resultado, se determinadas operações puderem ser executadas durante o processo de compilação e, assim, remover a carga do sistema durante a execução do programa, isso terá um bom efeito no comportamento do programa durante a execução. Aqui está um exemplo:
#include <iostream> constexpr long long fact(long long n) { // constexpr return n == 1 ? 1 : (fact(n-1) * n); } int main() { const long long bigval = fact(20); std::cout<<bigval<<std::endl; }
Este é um exemplo muito comum de uso do
constexpr
.
Como declaramos a função para calcular o fatorial como
constexpr
, o compilador pode pré-calcular o valor do
fact(20)
no momento da compilação do programa. Como resultado, após a compilação, a string
const long long bigval = fact(20);
pode ser substituído por
const long long bigval = 2432902008176640000;
.
Observe que o argumento passado para a função é representado por uma constante. Esse é um recurso importante do uso de funções declaradas usando a
constexpr
. Os argumentos passados para eles também devem ser declarados com a
constexpr
-
constexpr
ou com a palavra-chave
const
. Caso contrário, essas funções se comportarão como funções comuns, ou seja, durante a compilação, seus valores não serão calculados com antecedência.
As variáveis também podem ser declaradas usando a
constexpr
. Nesse caso, como você pode imaginar, os valores dessas variáveis devem ser calculados em tempo de compilação. Se isso não puder ser feito, uma mensagem de erro de compilação será exibida.
É interessante notar que, mais tarde, em C ++ 17, as construções
constexpr-if e
constexpr-lambda apareceram.
Estruturas de dados de tupla
Como a estrutura de dados do
pair
a estrutura de dados da
tuple
(tupla) é uma coleção de valores de diferentes tipos de tamanho fixo. Aqui está um exemplo:
auto user_info = std::make_tuple("M", "Chowdhury", 25);
Às vezes, em vez de uma estrutura de dados de
tuple
, é mais conveniente usar
std::array
. Essa estrutura de dados é semelhante às matrizes simples usadas na linguagem C, equipadas com recursos adicionais da biblioteca padrão C ++. Essa estrutura de dados apareceu no C ++ 11.
Inferência automática do tipo de argumento do modelo de classe
O nome desse recurso parece bastante longo e complexo, mas na verdade não há nada complicado aqui. A idéia principal aqui é que, no C ++ 17, a saída de tipos de argumentos de modelo também é executada para modelos de classe padrão. Anteriormente, isso era suportado apenas para modelos funcionais. Como resultado, acontece que eles costumavam escrever assim:
std::pair<std::string, int> user = {"M", 25};
Com o lançamento do C ++ 17, essa construção agora pode ser substituída por:
std::pair user = {"M", 25};
A inferência de tipo é feita implicitamente. Esse mecanismo é ainda mais conveniente quando se trata de tuplas. Ou seja, antes eu tinha que escrever o seguinte:
std::tuple<std::string, std::string, int> user ("M", "Chy", 25);
Agora, a mesma coisa se parece com isso:
std::tuple user2("M", "Chy", 25);
Vale ressaltar que esses recursos não parecerão algo digno de atenção para aqueles que não estão particularmente familiarizados com os modelos C ++.
Ponteiros inteligentes
Trabalhar com ponteiros em C ++ pode ser um verdadeiro pesadelo. Graças à liberdade que a linguagem oferece ao programador, às vezes é muito difícil para ele, como se costuma dizer, "não dar um tiro no pé". Em muitos casos, os indicadores estão pressionando por uma "chance" do programador.
Felizmente, o C ++ 11 introduziu indicadores inteligentes que são muito mais convenientes do que indicadores comuns. Eles ajudam o programador a evitar vazamentos de memória, liberando recursos quando possível. Além disso, eles fornecem uma garantia de segurança para exceções.
Sumário
Aqui está um bom repositório, que, acreditamos, será interessante para quem seguir as inovações do C ++. Algo novo está constantemente aparecendo nesse idioma. Aqui abordamos apenas alguns recursos modernos do idioma. De fato, existem muitos deles. É possível que ainda falemos sobre eles.
Caros leitores! Quais recursos modernos do C ++ você considera mais interessantes e úteis?
