C ++ 11 - C++11

C ++ 11 é uma versão do padrão ISO / IEC 14882 para a linguagem de programação C ++ . O C ++ 11 substituiu a versão anterior do padrão C ++, chamado C ++ 03 , e foi posteriormente substituído pelo C ++ 14 . O nome segue a tradição de nomear versões de idioma pelo ano de publicação da especificação, embora tenha sido anteriormente denominado C ++ 0x porque era esperado que fosse publicado antes de 2010.

Embora um dos objetivos do projeto fosse preferir mudanças nas bibliotecas em vez de mudanças na linguagem central , o C ++ 11 faz várias adições à linguagem central. As áreas da linguagem principal que foram significativamente aprimoradas incluem suporte a multithreading, suporte de programação genérica , inicialização uniforme e desempenho. Mudanças significativas também foram feitas para a biblioteca padrão C ++ , incorporando a maioria dos C Relatório Técnico ++ 1 (TR1) bibliotecas , exceto a biblioteca de funções especiais matemáticos.

C ++ 11 foi publicado como ISO / IEC 14882: 2011 em setembro de 2011 e está disponível por uma taxa. O rascunho de trabalho mais semelhante ao padrão C ++ 11 publicado é o N3337, datado de 16 de janeiro de 2012; possui apenas correções editoriais do padrão C ++ 11.

Metas de design

O comitê de projeto tentou cumprir uma série de objetivos ao projetar C ++ 11:

  • Manter a estabilidade e compatibilidade com C ++ 98 e possivelmente com C
  • Prefira introduzir novos recursos por meio da biblioteca padrão, em vez de estender a linguagem principal
  • Prefira mudanças que podem evoluir a técnica de programação
  • Aprimore o C ++ para facilitar o design de sistemas e bibliotecas, em vez de introduzir novos recursos úteis apenas para aplicativos específicos
  • Aumente a segurança de tipo, fornecendo alternativas mais seguras para técnicas anteriores inseguras
  • Aumente o desempenho e a capacidade de trabalhar diretamente com o hardware
  • Fornece soluções adequadas para problemas do mundo real
  • Implementar o princípio de sobrecarga zero (suporte adicional necessário para alguns utilitários deve ser usado apenas se o utilitário for usado)
  • Torne C ++ fácil de ensinar e aprender sem remover nenhum utilitário necessário para programadores experientes

A atenção aos iniciantes é considerada importante, porque a maioria dos programadores de computador sempre o será, e porque muitos iniciantes nunca ampliam seus conhecimentos, limitando-se a trabalhar em aspectos da linguagem em que se especializam.

Extensões para a linguagem central C ++

Uma função do comitê C ++ é o desenvolvimento do núcleo da linguagem. As áreas da linguagem principal que foram significativamente aprimoradas incluem suporte a multithreading , suporte de programação genérica , inicialização uniforme e desempenho.

Aprimoramentos de desempenho de tempo de execução da linguagem principal

Esses recursos de linguagem existem principalmente para fornecer algum tipo de benefício de desempenho, seja de memória ou de velocidade computacional.

Referências de valor R e construtores de movimento

Em C ++ 03 (e antes), os temporários (denominados " rvalues ", já que frequentemente ficam do lado direito de uma atribuição) foram concebidos para nunca serem modificáveis ​​- assim como em C - e foram considerados indistinguíveis dos const T&tipos; no entanto, em alguns casos, os temporários podem ter sido modificados, um comportamento que foi até considerado uma brecha útil. C ++ 11 adiciona um novo tipo de referência não const chamado dereferência de rvalue , identificada porT&&. Refere-se a temporários que podem ser modificados após serem inicializados, com o propósito de permitir "mover semântica".

Um problema crônico de desempenho com C ++ 03 são as cópias profundas caras e desnecessárias que podem acontecer implicitamente quando os objetos são passados ​​por valor. Para ilustrar o problema, considere que um std::vector<T>é, internamente, um invólucro em torno de uma matriz de estilo C com um tamanho definido. Se um std::vector<T>temporário é criado ou retornado de uma função, ele pode ser armazenado apenas criando um novo std::vector<T>e copiando todos os dados de rvalue nele. Então o temporário e toda a sua memória são destruídos. (Para simplificar, esta discussão negligencia a otimização do valor de retorno .)

Em C ++ 11, um move o construtor destd::vector<T>que leva uma referência de rvalue para umstd::vector<T>pode copiar o ponteiro para a matriz interna de estilo C de rvalue para o novo estd::vector<T>, em seguida, definir o ponteiro dentro de rvalue como null. Como o temporário nunca mais será usado, nenhum código tentará acessar o ponteiro nulo e, como o ponteiro é nulo, sua memória não é excluída quando ele sai do escopo. Conseqüentemente, a operação não apenas dispensa as despesas de uma cópia profunda, mas é segura e invisível.

As referências de Rvalue podem fornecer benefícios de desempenho para o código existente sem a necessidade de fazer alterações fora da biblioteca padrão. O tipo do valor retornado de uma função que retorna um std::vector<T>temporário não precisa ser alterado explicitamente para std::vector<T> &&para chamar o construtor de movimento, pois os temporários são considerados rvalues ​​automaticamente. (No entanto, se std::vector<T>for uma versão C ++ 03 sem um construtor de movimentação, o construtor de cópia será chamado com um const std::vector<T>&, incorrendo em uma alocação de memória significativa.)

Por razões de segurança, algumas restrições são impostas. Uma variável nomeada nunca será considerada um rvalue, mesmo se for declarada como tal. Para obter um rvalue, o modelo de função std::move()deve ser usado. As referências de Rvalue também podem ser modificadas apenas em certas circunstâncias, sendo destinadas a serem usadas principalmente com construtores de movimento.

Devido à natureza do texto das referências de rvalue e a algumas modificações no texto das referências de lvalue (referências regulares), as referências de rvalue permitem que os desenvolvedores forneçam encaminhamento de função perfeito. Quando combinada com modelos variados , essa capacidade permite modelos de função que podem encaminhar argumentos perfeitamente para outra função que usa esses argumentos específicos. Isso é mais útil para encaminhar parâmetros do construtor, para criar funções de fábrica que chamarão automaticamente o construtor correto para esses argumentos específicos. Isso é visto no conjunto emplace_back dos métodos de biblioteca padrão C ++.

constexpr - Expressões constantes generalizadas

C ++ sempre teve o conceito de expressões constantes. Essas são expressões como 3+4aquela que sempre produzirão os mesmos resultados, em tempo de compilação e em tempo de execução. Expressões constantes são oportunidades de otimização para compiladores, e os compiladores frequentemente as executam em tempo de compilação e codificam os resultados no programa. Além disso, em vários lugares, a especificação C ++ requer o uso de expressões constantes. Definir uma matriz requer uma expressão constante e os valores do enumerador devem ser expressões constantes.

No entanto, uma expressão constante nunca teve permissão para conter uma chamada de função ou construtor de objeto. Portanto, um trecho de código tão simples quanto este é inválido:

int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++

Isso não era válido em C ++ 03, porque get_five() + 7não é uma expressão constante. Um compilador C ++ 03 não tem como saber se get_five()realmente é constante em tempo de execução. Em teoria, esta função pode afetar uma variável global, chamar outras funções constantes de tempo não de execução, etc.

C ++ 11 introduziu a palavra-chave constexpr, que permite ao usuário garantir que uma função ou construtor de objeto é uma constante de tempo de compilação. O exemplo acima pode ser reescrito da seguinte forma:

constexpr int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Valid C++11

Isso permite que o compilador entenda e verifique se get_five()é uma constante de tempo de compilação.

Usar constexprem uma função impõe alguns limites sobre o que essa função pode fazer. Primeiro, a função deve ter um tipo de retorno não nulo. Em segundo lugar, o corpo da função não pode declarar variáveis ​​ou definir novos tipos. Terceiro, o corpo pode conter apenas declarações, instruções nulas e uma única instrução de retorno. Deve haver valores de argumento de forma que, após a substituição do argumento, a expressão na instrução de retorno produza uma expressão constante.

Antes do C ++ 11, os valores das variáveis ​​podiam ser usados ​​em expressões constantes apenas se as variáveis ​​fossem declaradas const, tivessem um inicializador que fosse uma expressão constante e fossem do tipo integral ou enumeração. C ++ 11 remove a restrição de que as variáveis ​​devem ser do tipo integral ou enumeração se forem definidas com a constexprpalavra-chave:

constexpr double earth_gravitational_acceleration = 9.8;
constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0;

Essas variáveis ​​de dados são implicitamente const e devem ter um inicializador que deve ser uma expressão constante.

Para construir valores de dados de expressão constante a partir de tipos definidos pelo usuário, os construtores também podem ser declarados com constexpr. O constexprcorpo da função de um construtor pode conter apenas declarações e instruções nulas e não pode declarar variáveis ​​ou definir tipos, como acontece com uma constexprfunção. Deve haver valores de argumento de forma que, após a substituição do argumento, ele inicialize os membros da classe com expressões constantes. Os destruidores de tais tipos devem ser triviais.

O construtor de cópia para um tipo com qualquer constexprconstrutor geralmente também deve ser definido como um constexprconstrutor, para permitir que objetos do tipo sejam retornados por valor de uma função constexpr. Qualquer função de membro de uma classe, como construtores de cópia, sobrecargas de operador, etc., pode ser declarada como constexpr, desde que atenda aos requisitos para funções constexpr. Isso permite que o compilador copie objetos em tempo de compilação, execute operações neles, etc.

Se uma função ou construtor constexpr for chamado com argumentos que não são expressões constantes, a chamada se comporta como se a função não fosse constexpr e o valor resultante não fosse uma expressão constante. Da mesma forma, se a expressão na instrução return de uma função constexpr não for avaliada como uma expressão constante para uma determinada invocação, o resultado não será uma expressão constante.

constexprdifere de consteval, introduzido em C ++ 20 , em que este último deve sempre produzir uma constante de tempo de compilação, embora constexprnão tenha essa restrição.

Modificação na definição de dados simples e antigos

Em C ++ 03, uma classe ou estrutura deve seguir uma série de regras para ser considerada um tipo de dados antigos simples (POD). Os tipos que se enquadram nessa definição produzem layouts de objeto compatíveis com C e também podem ser inicializados estaticamente. O padrão C ++ 03 tem restrições sobre quais tipos são compatíveis com C ou podem ser inicializados estaticamente, apesar de não haver razão técnica para que o compilador não possa aceitar o programa; se alguém criasse um tipo de POD C ++ 03 e adicionasse uma função de membro não virtual, esse tipo não seria mais um tipo de POD, não poderia ser inicializado estaticamente e seria incompatível com C, apesar de nenhuma alteração no layout da memória .

C ++ 11 relaxou várias das regras de POD, dividindo o conceito de POD em dois conceitos separados: trivial e layout padrão .

Um tipo trivial pode ser inicializado estaticamente. Também significa que é válido copiar dados por meio de memcpy, em vez de ter que usar um construtor de cópia. O tempo de vida de um tipo trivial começa quando seu armazenamento é definido, não quando um construtor é concluído.

Uma classe ou estrutura trivial é definida como aquela que:

  1. Possui um construtor padrão trivial. Isso pode usar a sintaxe do construtor padrão ( SomeConstructor() = default;).
  2. Possui construtores triviais de copiar e mover, que podem usar a sintaxe padrão.
  3. Possui operadores de atribuição triviais para copiar e mover, que podem usar a sintaxe padrão.
  4. Possui um destruidor trivial, que não deve ser virtual.

Os construtores são triviais apenas se não houver funções-membro virtuais da classe e nenhuma classe base virtual. As operações de copiar / mover também exigem que todos os membros de dados não estáticos sejam triviais.

Um tipo de layout padrão significa que ele ordena e empacota seus membros de uma forma que seja compatível com C. Uma classe ou estrutura é um layout padrão, por definição, desde:

  1. Não tem funções virtuais
  2. Não tem classes de base virtual
  3. Todos os seus membros de dados não estáticos têm o mesmo controle de acesso (público, privado, protegido)
  4. Todos os seus membros de dados não estáticos, incluindo qualquer um em suas classes base, estão na mesma classe na hierarquia
  5. As regras acima também se aplicam a todas as classes básicas e a todos os membros de dados não estáticos na hierarquia de classes
  6. Não tem classes básicas do mesmo tipo que o primeiro membro de dados não estático definido

Uma classe / estrutura / união é considerada POD se for trivial, de layout padrão e todos os seus membros de dados não estáticos e classes base forem PODs.

Ao separar esses conceitos, torna-se possível abrir mão de um sem perder o outro. Uma classe com construtores de mover e copiar complexos pode não ser trivial, mas poderia ser de layout padrão e, portanto, interoperar com C. Da mesma forma, uma classe com membros de dados não estáticos públicos e privados não seria de layout padrão, mas poderia ser trivial e, portanto, memcpyfácil.

Aprimoramentos de desempenho de tempo de construção da linguagem principal

Template externo

No C ++ 03, o compilador deve instanciar um modelo sempre que um modelo totalmente especificado for encontrado em uma unidade de tradução. Se o modelo for instanciado com os mesmos tipos em muitas unidades de tradução, isso pode aumentar drasticamente os tempos de compilação. Não há como evitar isso no C ++ 03, portanto, o C ++ 11 introduziu declarações de modelo externas, análogas às declarações de dados externos.

C ++ 03 tem esta sintaxe para obrigar o compilador a instanciar um modelo:

template class std::vector<MyClass>;

C ++ 11 agora fornece esta sintaxe:

extern template class std::vector<MyClass>;

que diz ao compilador para não instanciar o modelo nesta unidade de tradução.

Melhorias de usabilidade da linguagem principal

Esses recursos existem com o objetivo principal de tornar a linguagem mais fácil de usar. Isso pode melhorar a segurança de tipo, minimizar a repetição de código, tornar o código errado menos provável, etc.

Listas de inicializadores

C ++ 03 herdou o recurso de lista de inicializadores de C. Uma estrutura ou matriz recebe uma lista de argumentos entre chaves, na ordem das definições dos membros na estrutura. Essas listas de inicializadores são recursivas, portanto, um array de structs ou struct contendo outras structs pode usá-las.

struct Object
{
    float first;
    int second;
};

Object scalar = {0.43f, 10}; //One Object, with first=0.43f and second=10
Object anArray[] = {{13.4f, 3}, {43.28f, 29}, {5.934f, 17}}; //An array of three Objects

Isso é muito útil para listas estáticas ou para inicializar uma estrutura com algum valor. C ++ também fornece construtores para inicializar um objeto, mas geralmente não são tão convenientes quanto a lista de inicializadores. No entanto, C ++ 03 permite listas de inicializadores apenas em estruturas e classes que estão em conformidade com a definição de Dados Antigos Plain (POD); C ++ 11 estende as listas de inicializadores, para que possam ser usadas para todas as classes, incluindo contêineres padrão como std::vector.

C ++ 11 vincula o conceito a um modelo, chamado std::initializer_list. Isso permite que os construtores e outras funções tomem listas de inicializadores como parâmetros. Por exemplo:

class SequenceClass
{
public:
    SequenceClass(std::initializer_list<int> list);
};

Isso permite SequenceClassser construído a partir de uma sequência de inteiros, como:

SequenceClass some_var = {1, 4, 5, 6};

Este construtor é um tipo especial de construtor, chamado de construtor de lista de inicializador. As classes com tal construtor são tratadas especialmente durante a inicialização uniforme (veja abaixo )

A classe de modelo std::initializer_list<>é um tipo de biblioteca padrão C ++ 11 de primeira classe . Eles podem ser construídos estaticamente pelo compilador C ++ 11 por meio do uso da {}sintaxe sem um nome de tipo em contextos onde tais chaves serão deduzidas a um std::initializer_list, ou especificando explicitamente o tipo como std::initializer_list<SomeType>{args}(e assim por diante para outras variedades de sintaxe de construção).

A lista pode ser copiada depois de construída, o que é barato e funcionará como uma cópia por referência (a classe é normalmente implementada como um par de ponteiros de início / fim). Um std::initializer_listé constante: seus membros não podem ser alterados depois de criado, e nem os dados desses membros podem ser alterados (o que exclui a movimentação deles, exigindo cópias para os membros da classe, etc.).

Embora sua construção seja especialmente tratada pelo compilador, an std::initializer_listé um tipo real e, portanto, pode ser usado em outros locais além dos construtores de classes. Funções regulares podem receber std::initializer_lists digitados como argumentos. Por exemplo:

void function_name(std::initializer_list<float> list); // Copying is cheap; see above

function_name({1.0f, -3.45f, -0.4f});

Exemplos disso na biblioteca padrão incluem os modelos std::min()e de tipo numérico. std::max()std::initializer_list

Os contêineres padrão também podem ser inicializados das seguintes maneiras:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v({ "xyzzy", "plugh", "abracadabra" });
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; // see "Uniform initialization" below

Inicialização uniforme

C ++ 03 tem vários problemas com a inicialização de tipos. Existem várias maneiras de fazer isso e algumas produzem resultados diferentes quando trocadas. A sintaxe do construtor tradicional, por exemplo, pode parecer uma declaração de função, e etapas devem ser executadas para garantir que a regra de análise mais incômoda do compilador não a confunda com tal. Apenas agregados e tipos de POD podem ser inicializados com inicializadores de agregação (usando SomeType var = {/*stuff*/};).

C ++ 11 fornece uma sintaxe que permite a inicialização de tipo totalmente uniforme que funciona em qualquer objeto. Ele expande a sintaxe da lista de inicializadores:

struct BasicStruct
{
    int x;
    double y;
};

struct AltStruct
{
    AltStruct(int x, double y)
        : x_{x}
        , y_{y}
    {}

private:
    int x_;
    double y_;
};

BasicStruct var1{5, 3.2};
AltStruct var2{2, 4.3};

A inicialização de var1se comporta exatamente como se fosse uma inicialização de agregação. Ou seja, cada membro de dados de um objeto, por sua vez, será inicializado por cópia com o valor correspondente da lista de inicializadores. A conversão implícita de tipo será usada quando necessário. Se não houver conversão, ou apenas uma conversão de estreitamento, o programa está malformado. A inicialização de var2invoca o construtor.

Também se pode fazer isso:

struct IdString
{
    std::string name;
    int identifier;
};

IdString get_string()
{
    return {"foo", 42}; //Note the lack of explicit type.
}

A inicialização uniforme não substitui a sintaxe do construtor, que ainda é necessária às vezes. Se uma classe tiver um construtor de lista de inicializadores ( TypeName(initializer_list<SomeType>);), ela terá prioridade sobre outras formas de construção, desde que a lista de inicializadores esteja em conformidade com o tipo do construtor de sequência. A versão C ++ 11 do std::vectortem um construtor de lista de inicializadores para seu tipo de modelo. Portanto, este código:

std::vector<int> the_vec{4};

irá chamar o construtor de lista de inicializadores, não o construtor de std::vectorque pega um único parâmetro de tamanho e cria o vetor com aquele tamanho. Para acessar o último construtor, o usuário precisará usar a sintaxe do construtor padrão diretamente.

Inferência de tipo

Em C ++ 03 (e C), para usar uma variável, seu tipo deve ser especificado explicitamente. No entanto, com o advento de tipos de modelo e técnicas de metaprogramação de modelo, o tipo de algo, particularmente o valor de retorno bem definido de uma função, pode não ser expresso facilmente. Assim, armazenar intermediários em variáveis ​​é difícil, possivelmente necessitando de conhecimento dos internos de uma dada biblioteca de metaprogramação.

C ++ 11 permite que isso seja mitigado de duas maneiras. Primeiro, a definição de uma variável com uma inicialização explícita pode usar a autopalavra - chave. Isso cria uma variável do tipo específico de inicializador:

auto some_strange_callable_type = std::bind(&some_function, _2, _1, some_object);
auto other_variable = 5;

O tipo de some_strange_callable_typeé simplesmente qualquer que seja a substituição de função de modelo específica de std::bindretornos para esses argumentos específicos. Esse tipo é facilmente determinado por procedimento pelo compilador como parte de suas funções de análise semântica, mas não é fácil para o usuário determinar durante a inspeção. O tipo de other_variabletambém está bem definido, mas é mais fácil para o usuário determinar. É um int, que é do mesmo tipo que o literal inteiro.

Esse uso da palavra-chave autoem C ++ redefine a semântica dessa palavra-chave, que foi originalmente usada na linguagem predecessora sem tipo B em uma função relacionada de denotar uma definição de variável automática sem tipo .

Além disso, a palavra-chave decltypepode ser usada para determinar o tipo de expressão em tempo de compilação. Por exemplo:

int some_int;
decltype(some_int) other_integer_variable = 5;

Isso é mais útil em conjunto com auto, uma vez que o tipo de variável automática é conhecido apenas pelo compilador. No entanto, decltypetambém pode ser muito útil para expressões em código que fazem uso intenso de sobrecarga de operador e tipos especializados.

autotambém é útil para reduzir o detalhamento do código. Por exemplo, em vez de escrever

for (std::vector<int>::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr)

o programador pode usar o mais curto

for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr)

que pode ser ainda mais compactado, uma vez que "myvec" implementa iteradores de início / fim:

for (const auto& x : myvec)

Essa diferença aumenta à medida que o programador começa a aninhar contêineres, embora, em tais casos, typedefs sejam uma boa maneira de diminuir a quantidade de código.

O tipo denotado por decltypepode ser diferente do tipo deduzido por auto.

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a has type int
    decltype(v[0]) b = 1; // b has type const int&, the return type of
                          //   std::vector<int>::operator[](size_type) const
    auto c = 0;           // c has type int
    auto d = c;           // d has type int
    decltype(c) e;        // e has type int, the type of the entity named by c
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue
    decltype(0) g;        // g has type int, because 0 is an rvalue
}

Com base em intervalo para loop

C ++ 11 estende a sintaxe da forinstrução para permitir uma iteração fácil em uma variedade de elementos:

int my_array[5] = {1, 2, 3, 4, 5};
// double the value of each element in my_array:
for (int& x : my_array)
    x *= 2;

// similar but also using type inference for array elements
for (auto& x : my_array)
    x *= 2;

Essa forma de for, chamada de “baseado em intervalo para”, irá iterar sobre cada elemento da lista. Ele funcionará para arrays de estilo C, listas de inicializadores e qualquer tipo que tenha begin()e end()funções definidas para ele que retornem iteradores. Todos os contêineres de biblioteca padrão que têm pares de início / fim funcionarão com a instrução for baseada em intervalo.

Funções e expressões lambda

C ++ 11 fornece a capacidade de criar funções anônimas , chamadas funções lambda. Eles são definidos da seguinte forma:

[](int x, int y) -> int { return x + y; }

O tipo de retorno ( -> intneste exemplo) pode ser omitido, desde que todas as returnexpressões retornem o mesmo tipo. Um lambda pode ser opcionalmente um fechamento .

Sintaxe de função alternativa

A sintaxe de declaração de função C padrão era perfeitamente adequada para o conjunto de recursos da linguagem C. Como o C ++ evoluiu do C, ele manteve a sintaxe básica e a estendeu quando necessário. No entanto, conforme o C ++ se tornou mais complexo, ele expôs vários limites, especialmente em relação às declarações de função de modelo. Por exemplo, em C ++ 03, isso não é permitido:

template<class Lhs, class Rhs>
  Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret must be the type of lhs+rhs

O tipo Reté qualquer que seja a adição de tipos Lhse Rhsproduzirá. Mesmo com a funcionalidade do C ++ 11 mencionada acima decltype, isso não é possível:

template<class Lhs, class Rhs>
  decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Not valid C++11

Isso não é C ++ válido porque lhse rhsainda não foi definido; eles não serão identificadores válidos até que o analisador tenha analisado o resto do protótipo da função.

Para contornar isso, C ++ 11 introduziu uma nova sintaxe de declaração de função, com um tipo de retorno à direita :

template<class Lhs, class Rhs>
  auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

Esta sintaxe pode ser usada para declarações e definições de funções mais comuns:

struct SomeStruct
{
    auto func_name(int x, int y) -> int;
};

auto SomeStruct::func_name(int x, int y) -> int
{
    return x + y;
}

O uso da palavra-chave “auto” neste caso é apenas parte da sintaxe e não executa a dedução automática de tipo em C ++ 11. No entanto, começando com C ++ 14, o tipo de retorno final pode ser removido completamente e o compilador deduzirá o tipo de retorno automaticamente.

Melhoria de construção de objeto

Em C ++ 03, os construtores de uma classe não têm permissão para chamar outros construtores em uma lista de inicializadores dessa classe. Cada construtor deve construir todos os seus próprios membros de classe ou chamar uma função de membro comum, da seguinte maneira:

class SomeType
{
public:
    SomeType(int new_number)
    {
        Construct(new_number);
    }

    SomeType()
    {
        Construct(42);
    }

private:
    void Construct(int new_number)
    {
        number = new_number;
    }

    int number;
};

Construtores de classes básicas não podem ser expostos diretamente a classes derivadas; cada classe derivada deve implementar construtores mesmo se um construtor de classe base for apropriado. Membros de classes de dados não constantes não podem ser inicializados no site da declaração desses membros. Eles podem ser inicializados apenas em um construtor.

C ++ 11 fornece soluções para todos esses problemas.

C ++ 11 permite que os construtores chamem outros construtores de pares (denominado delegação ). Isso permite que os construtores utilizem o comportamento de outro construtor com um mínimo de código adicionado. A delegação foi usada em outras linguagens, por exemplo, Java e Objective-C .

Essa sintaxe é a seguinte:

class SomeType
{
    int number;

public:
    SomeType(int new_number) : number(new_number) {}
    SomeType() : SomeType(42) {}
};

Observe que, neste caso, o mesmo efeito poderia ter sido obtido definindo new_numberum parâmetro padrão. A nova sintaxe, no entanto, permite que o valor padrão (42) seja expresso na implementação em vez da interface - um benefício para os mantenedores do código da biblioteca, uma vez que os valores padrão para os parâmetros de função são "embutidos" nos sites de chamada, enquanto a delegação do construtor permite o valor a ser alterado sem recompilação do código usando a biblioteca.

Isso vem com uma advertência: C ++ 03 considera um objeto a ser construído quando seu construtor termina a execução, mas C ++ 11 considera um objeto construído quando qualquer construtor termina a execução. Visto que vários construtores terão permissão para executar, isso significa que cada construtor delegado estará executando em um objeto totalmente construído de seu próprio tipo. Os construtores de classes derivadas serão executados após a conclusão de toda a delegação em suas classes básicas.

Para construtores de classe base, C ++ 11 permite que uma classe especifique que os construtores de classe base serão herdados. Assim, o compilador C ++ 11 irá gerar código para realizar a herança e o encaminhamento da classe derivada para a classe base. Este é um recurso tudo ou nada: ou todos os construtores dessa classe base são encaminhados ou nenhum deles é. Além disso, um construtor herdado será sombreado se corresponder à assinatura de um construtor da classe derivada, e existem restrições para herança múltipla: os construtores de classe não podem ser herdados de duas classes que usam construtores com a mesma assinatura .

A sintaxe é a seguinte:

class BaseClass
{
public:
    BaseClass(int value);
};

class DerivedClass : public BaseClass
{
public:
    using BaseClass::BaseClass;
};

Para inicialização de membro, C ++ 11 permite esta sintaxe:

class SomeClass
{
public:
    SomeClass() {}
    explicit SomeClass(int new_value) : value(new_value) {}

private:
    int value = 5;
};

Qualquer construtor da classe será inicializado valuecom 5, se o construtor não substituir a inicialização pela sua própria. Portanto, o construtor vazio acima será inicializado valueconforme os estados de definição da classe, mas o construtor que recebe um int irá inicializá-lo com o parâmetro fornecido.

Ele também pode usar o construtor ou inicialização uniforme, em vez da inicialização de atribuição mostrada acima.

Sobreposições explícitas e finais

Em C ++ 03, é possível criar acidentalmente uma nova função virtual, quando se pretende substituir uma função de classe base. Por exemplo:

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int);
};

Suponha que o Derived::some_funcobjetivo seja substituir a versão da classe base. Mas em vez disso, por ter uma assinatura diferente , ele cria uma segunda função virtual. Este é um problema comum, principalmente quando um usuário vai modificar a classe base.

C ++ 11 fornece sintaxe para resolver esse problema.

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

O overrideidentificador especial significa que o compilador verificará a (s) classe (s) base para ver se há uma função virtual com essa assinatura exata. E se não houver, o compilador indicará um erro.

C ++ 11 também adiciona a capacidade de impedir a herança de classes ou simplesmente impedir a substituição de métodos em classes derivadas. Isso é feito com o identificador especial final. Por exemplo:

struct Base1 final { };

struct Derived1 : Base1 { }; // ill-formed because the class Base1 has been marked final
struct Base2
{
    virtual void f() final;
};

struct Derived2 : Base2
{
    void f(); // ill-formed because the virtual function Base2::f has been marked final
};

Neste exemplo, a virtual void f() final;instrução declara uma nova função virtual, mas também evita que classes derivadas a substituam. Ele também tem o efeito de impedir que classes derivadas usem aquele nome de função e combinação de parâmetros em particular.

Observe que nem overridenem finalsão palavras-chave de idioma. Eles são tecnicamente identificadores para atributos do declarador:

  • eles ganham significado especial como atributos apenas quando usados ​​nesses contextos finais específicos (depois de todos os especificadores de tipo, especificadores de acesso, declarações de membro (para tipos de estrutura, classe e enum) e especificadores de declarador, mas antes da inicialização ou implementação de código de cada declarador em uma vírgula -lista separada de declaradores);
  • eles não alteram a assinatura de tipo declarada e não declaram ou substituem qualquer novo identificador em qualquer escopo;
  • os atributos de declarador reconhecidos e aceitos podem ser estendidos em versões futuras de C ++ (algumas extensões específicas do compilador já reconhecem atributos de declarador adicionados, para fornecer opções de geração de código ou dicas de otimização para o compilador, ou para gerar dados adicionados no código compilado, destinado a depuradores, vinculadores e implantação do código compilado, ou para fornecer atributos de segurança específicos do sistema adicionados, ou para aprimorar as habilidades de reflexão em tempo de execução, ou para fornecer informações de ligação adicionais para interoperabilidade com outras linguagens de programação e sistemas de tempo de execução; essas extensões podem ter parâmetros entre parênteses após o identificador de atributo do declarador; para conformidade ANSI, essas extensões específicas do compilador devem usar a convenção de prefixo de sublinhado duplo).
  • Em qualquer outro local, eles podem ser identificadores válidos para novas declarações (e uso posterior se estiverem acessíveis).

Constante de ponteiro nulo

Para os fins desta seção e apenas desta seção, cada ocorrência de “ 0” é considerada como “uma expressão constante que avalia para 0, que é do tipo int”. Na realidade, a expressão constante pode ser de qualquer tipo integral.

Desde o surgimento de C em 1972, a constante 0teve o papel duplo de constante de número inteiro e de ponteiro nulo. A ambigüidade inerente ao duplo significado de 0foi tratada em C usando a macro do pré-processador NULL, que comumente se expande para ((void*)0)ou 0. C ++ proíbe a conversão implícita de void *para outros tipos de ponteiro, removendo assim o benefício de converter 0para void *. Como consequência, só 0é permitido como uma constante de ponteiro nulo. Isso interage mal com a sobrecarga de funções :

void foo(char *);
void foo(int);

Se NULLfor definido como 0(o que geralmente é o caso em C ++), a instrução foo(NULL);chamará foo(int), o que quase certamente não é o que o programador pretendia e não o que uma leitura superficial do código sugere.

C ++ 11 corrige este através da introdução de uma nova palavra-chave para servir como um distinto constante ponteiro nulo: nullptr. É do tipo nullptr_timplicitamente conversível e comparável a qualquer tipo de ponteiro ou tipo de ponteiro para membro. Não é implicitamente conversível ou comparável a tipos integrais, exceto para bool. Embora a proposta original especifique que um rvalue do tipo nullptr_tnão deve ser conversível para bool, o grupo de trabalho da linguagem principal decidiu que tal conversão seria desejável, para consistência com os tipos de ponteiro regulares. As alterações de redação propostas foram votadas por unanimidade no Documento de Trabalho em junho de 2008. Uma proposta semelhante também é apresentada ao grupo de trabalho C padrão.

Por motivos de compatibilidade com versões anteriores, 0permanece uma constante de ponteiro nula válida.

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b is false.
int    i = nullptr;     // error

foo(nullptr);           // calls foo(nullptr_t), not foo(int);
/*
  Note that foo(nullptr_t) will actually call foo(char *) in the example above using an implicit conversion,
  only if no other functions are overloading with compatible pointer types in scope.
  If multiple overloadings exist, the resolution will fail as it is ambiguous,
  unless there is an explicit declaration of foo(nullptr_t).

  In standard types headers for C++11, the nullptr_t  type should be declared as:
      typedef decltype(nullptr) nullptr_t;
  but not as:
      typedef int nullptr_t; // prior versions of C++ which need NULL to be defined as 0
      typedef void *nullptr_t; // ANSI C which defines NULL as ((void*)0)
*/

Enumerações fortemente tipadas

No C ++ 03, as enumerações não são seguras para o tipo. Eles são efetivamente inteiros, mesmo quando os tipos de enumeração são distintos. Isso permite a comparação entre dois valores de enum de diferentes tipos de enumeração. A única segurança que o C ++ 03 fornece é que um número inteiro ou um valor de um tipo de enum não é convertido implicitamente em outro tipo de enum. Além disso, o tipo integral subjacente é definido pela implementação; o código que depende do tamanho da enumeração não é portável. Por último, os valores de enumeração têm como escopo o escopo delimitador. Portanto, não é possível que duas enumerações separadas no mesmo escopo tenham nomes de membros correspondentes.

C ++ 11 permite uma classificação especial de enumeração que não tem nenhum desses problemas. Isso é expresso usando a declaração enum class( enum structtambém é aceito como sinônimo):

enum class Enumeration
{
    Val1,
    Val2,
    Val3 = 100,
    Val4 // = 101
};

Essa enumeração é segura para o tipo. Os valores da classe Enum não são convertidos implicitamente em inteiros. Portanto, eles também não podem ser comparados a inteiros (a expressão Enumeration::Val4 == 101fornece um erro de compilação).

O tipo subjacente de classes enum é sempre conhecido. O tipo padrão é int; isso pode ser substituído por um tipo integral diferente, como pode ser visto neste exemplo:

enum class Enum2 : unsigned int {Val1, Val2};

Com as enumerações de estilo antigo, os valores são colocados no escopo externo. Com as enumerações de novo estilo, elas são colocadas dentro do escopo do nome da classe enum. Portanto, no exemplo acima, Val1é indefinido, mas Enum2::Val1está definido.

Há também uma sintaxe de transição para permitir que as enumerações de estilo antigo forneçam escopo explícito e a definição do tipo subjacente:

enum Enum3 : unsigned long {Val1 = 1, Val2};

Nesse caso, os nomes dos enumeradores são definidos no escopo da enumeração ( Enum3::Val1), mas para compatibilidade com versões anteriores, eles também são colocados no escopo delimitador.

Enums com declaração de encaminhamento também são possíveis no C ++ 11. Anteriormente, os tipos de enum não podiam ser declarados para frente porque o tamanho da enumeração depende da definição de seus membros. Contanto que o tamanho da enumeração seja especificado implícita ou explicitamente, ele pode ser declarado para frente:

enum Enum1;                      // Invalid in C++03 and C++11; the underlying type cannot be determined.
enum Enum2 : unsigned int;       // Valid in C++11, the underlying type is specified explicitly.
enum class Enum3;                // Valid in C++11, the underlying type is int.
enum class Enum4 : unsigned int; // Valid in C++11.
enum Enum2 : unsigned short;     // Invalid in C++11, because Enum2 was formerly declared with a different underlying type.

Colchete angular direito

O analisador de C ++ 03 define “ >>” como o operador de deslocamento à direita ou operador de extração de fluxo em todos os casos. No entanto, com declarações de modelo aninhadas, existe uma tendência para o programador negligenciar colocar um espaço entre os dois colchetes angulares retos, causando, assim, um erro de sintaxe do compilador.

C ++ 11 melhora a especificação do analisador para que vários colchetes angulares sejam interpretados como fechando a lista de argumentos do modelo onde for razoável. Isso pode ser substituído usando parênteses em torno de expressões de parâmetro usando os operadores binários “ >”, “ >=” ou “ >>”:

template<bool Test> class SomeType;
std::vector<SomeType<1>2>> x1;  // Interpreted as a std::vector of SomeType<true>,
    // followed by "2 >> x1", which is not valid syntax for a declarator. 1 is true.
std::vector<SomeType<(1>2)>> x1;  // Interpreted as std::vector of SomeType<false>,
    // followed by the declarator "x1", which is valid C++11 syntax. (1>2) is false.

Operadores de conversão explícita

C ++ 98 adicionou a explicitpalavra - chave como um modificador nos construtores para evitar que os construtores de argumento único sejam usados ​​como operadores de conversão de tipo implícito. No entanto, isso não faz nada para os operadores de conversão reais. Por exemplo, uma classe de ponteiro inteligente pode ter um operator bool()para permitir que ela atue mais como um ponteiro primitivo: se incluir esta conversão, pode ser testada com if (smart_ptr_variable)(o que seria verdadeiro se o ponteiro fosse não nulo e falso caso contrário). No entanto, isso permite outras conversões não intencionais também. Como C ++ boolé definido como um tipo aritmético, ele pode ser convertido implicitamente em tipos integrais ou mesmo de ponto flutuante, o que permite operações matemáticas que não são pretendidas pelo usuário.

No C ++ 11, a explicitpalavra - chave agora pode ser aplicada a operadores de conversão. Tal como acontece com os construtores, evita o uso dessas funções de conversão em conversões implícitas. No entanto, os contextos de linguagem que precisam especificamente de um valor booleano (as condições de instruções if e loops e operandos para os operadores lógicos) contam como conversões explícitas e podem, portanto, usar um operador de conversão booleano.

Por exemplo, esse recurso resolve de forma limpa o problema do bool seguro .

Aliases de modelo

Em C ++ 03, é possível definir um typedef apenas como um sinônimo para outro tipo, incluindo um sinônimo para uma especialização de modelo com todos os argumentos de modelo reais especificados. Não é possível criar um modelo de typedef. Por exemplo:

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
typedef SomeType<OtherType, Second, 5> TypedefName; // Invalid in C++03

Isso não vai compilar.

C ++ 11 adiciona essa capacidade com esta sintaxe:

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
using TypedefName = SomeType<OtherType, Second, 5>;

A usingsintaxe também pode ser usada como alias de tipo em C ++ 11:

typedef void (*FunctionType)(double);       // Old style
using FunctionType = void (*)(double); // New introduced syntax

Sindicatos irrestritos

Em C ++ 03, existem restrições sobre quais tipos de objetos podem ser membros de a union. Por exemplo, as uniões não podem conter nenhum objeto que defina um construtor ou destruidor não trivial. C ++ 11 elimina algumas dessas restrições.

Se um unionmembro tiver uma função de membro especial não trivial , o compilador não gerará a função de membro equivalente para o unione deve ser definida manualmente.

Este é um exemplo simples de união permitida em C ++ 11:

#include <new> // Needed for placement 'new'.

struct Point
{
    Point() {}
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_;
};

union U
{
    int z;
    double w;
    Point p; // Invalid in C++03; valid in C++11.
    U() {} // Due to the Point member, a constructor definition is now needed.
    U(const Point& pt) : p(pt) {} // Construct Point object using initializer list.
    U& operator=(const Point& pt) { new(&p) Point(pt); return *this; } // Assign Point object using placement 'new'.
};

As mudanças não irão quebrar nenhum código existente, pois elas apenas relaxam as regras atuais.

Melhorias na funcionalidade do idioma principal

Esses recursos permitem que a linguagem faça coisas que antes eram impossíveis, excessivamente prolixas ou que precisavam de bibliotecas não portáteis.

Modelos variados

No C ++ 11, os modelos podem ter números variáveis ​​de parâmetros de modelo. Isso também permite a definição de funções variáveis de tipo seguro .

Novos literais de string

C ++ 03 oferece dois tipos de literais de string . O primeiro tipo, contido entre aspas duplas, produz uma matriz de tipo terminada em nulo const char. O segundo tipo, definido como L"", produz uma matriz terminada em nulo do tipo const wchar_t, onde wchar_té um caractere largo de tamanho e semântica indefinidos. Nenhum tipo de literal oferece suporte para literais de string com UTF-8 , UTF-16 ou qualquer outro tipo de codificação Unicode .

A definição do tipo charfoi modificada para expressar explicitamente que tem pelo menos o tamanho necessário para armazenar uma codificação de oito bits de UTF-8 e grande o suficiente para conter qualquer membro do conjunto básico de caracteres de execução do compilador. Anteriormente, era definido como apenas o último no próprio padrão C ++, então contando com o padrão C para garantir pelo menos 8 bits.

C ++ 11 oferece suporte a três codificações Unicode: UTF-8 , UTF-16 e UTF-32 . Junto com as mudanças observadas anteriormente na definição de char, C ++ 11 adiciona dois novos tipos de caracteres: char16_te char32_t. Eles são projetados para armazenar UTF-16 e UTF-32, respectivamente.

A criação de literais de string para cada uma dessas codificações pode ser feita da seguinte maneira:

u8"I'm a UTF-8 string."
u"This is a UTF-16 string."
U"This is a UTF-32 string."

O tipo da primeira string é o usual const char[]. O tipo da segunda string é const char16_t[](observe o prefixo 'u' em minúsculas). O tipo da terceira string é const char32_t[](prefixo 'U' maiúsculo).

Ao construir literais de string Unicode, geralmente é útil inserir pontos de código Unicode diretamente na string. Para fazer isso, o C ++ 11 permite esta sintaxe:

u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \U00002018."

O número após o \ué um número hexadecimal; não precisa do 0xprefixo usual . O identificador \urepresenta um ponto de código Unicode de 16 bits; para inserir um ponto de código de 32 bits, use \Ue um número hexadecimal de 32 bits. Apenas pontos de código Unicode válidos podem ser inseridos. Por exemplo, pontos de código no intervalo U + D800 – U + DFFF são proibidos, pois são reservados para pares substitutos nas codificações UTF-16.

Às vezes, também é útil evitar o escape de strings manualmente, principalmente para usar literais de arquivos XML , linguagens de script ou expressões regulares. C ++ 11 fornece um literal de string bruto:

R"(The String Data \ Stuff " )"
R"delimiter(The String Data \ Stuff " )delimiter"

No primeiro caso, tudo entre o "(e )"faz parte da string. Os caracteres "e \não precisam ter escape. No segundo caso, o "delimiter(inicia a string, e só termina quando )delimiter"é alcançado. A string delimiterpode ser qualquer string de até 16 caracteres, incluindo a string vazia. Esta cadeia não pode conter espaços, caracteres de controle, (, ), ou o \personagem. O uso dessa string delimitadora permite que o usuário tenha )caracteres em literais de string bruta. Por exemplo, R"delimiter((a-z))delimiter"é equivalente a "(a-z)".

Literais de string brutos podem ser combinados com o literal largo ou qualquer um dos prefixos literais Unicode:

u8R"XXX(I'm a "raw UTF-8" string.)XXX"
uR"*(This is a "raw UTF-16" string.)*"
UR"(This is a "raw UTF-32" string.)"

Literais definidos pelo usuário

C ++ 03 fornece vários literais. Os caracteres 12.5são um literal que é resolvido pelo compilador como um tipo doublecom o valor de 12,5. No entanto, a adição do sufixo f, como em 12.5f, cria um valor do tipo floatque contém o valor 12,5. Os modificadores de sufixo para literais são fixados pela especificação C ++ e o código C ++ 03 não pode criar novos modificadores literais.

Em contraste, o C ++ 11 permite que o usuário defina novos tipos de modificadores literais que construirão objetos com base na sequência de caracteres que o literal modifica.

A transformação de literais é redefinida em duas fases distintas: cru e cozido. Um literal bruto é uma sequência de caracteres de algum tipo específico, enquanto o literal cozido é de um tipo separado. O C ++ literal 1234, como um literal em bruto, é a seguinte sequência de caracteres '1', '2', '3', '4'. Como um literal preparados, é o número inteiro 1234. O C ++ literal 0xAem forma bruta é '0', 'x', 'A', enquanto na forma de preparados é o número inteiro 10.

Os literais podem ser estendidos nas formas crua e cozida, com exceção dos literais de string, que podem ser processados ​​apenas na forma cozida. Essa exceção se deve ao fato de que as strings possuem prefixos que afetam o significado específico e o tipo dos caracteres em questão.

Todos os literais definidos pelo usuário são sufixos; definir literais de prefixo não é possível. Todos os sufixos que começam com qualquer caractere, exceto sublinhado ( _), são reservados pelo padrão. Portanto, todos os literais definidos pelo usuário devem ter sufixos começando com um sublinhado ( _).

Literais definidos pelo usuário que processam a forma bruta do literal são definidos por meio de um operador literal, que é escrito como operator "". Segue um exemplo:

OutputType operator "" _mysuffix(const char * literal_string)
{
    // assumes that OutputType has a constructor that takes a const char *
    OutputType ret(literal_string);
    return ret;
}

OutputType some_variable = 1234_mysuffix;
// assumes that OutputType has a get_value() method that returns a double
assert(some_variable.get_value() == 1234.0)

A instrução de atribuição OutputType some_variable = 1234_mysuffix;executa o código definido pela função literal definida pelo usuário. Essa função é passada "1234"como uma string de estilo C, portanto, tem um terminador nulo.

Um mecanismo alternativo para processar literais brutos de inteiros e de ponto flutuante é por meio de um modelo variável :

template<char...> OutputType operator "" _tuffix();

OutputType some_variable = 1234_tuffix;
OutputType another_variable = 2.17_tuffix;

Isso instancia a função de processamento literal como operator "" _tuffix<'1', '2', '3', '4'>(). Nesse formulário, não há caractere nulo encerrando a string. O objetivo principal para fazer isso é usar a constexprpalavra-chave do C ++ 11 para garantir que o compilador transformará o literal inteiramente no momento da compilação, assumindo que OutputTypeseja um tipo construtível e copiável constexpr e que a função de processamento literal seja uma constexprfunção.

Para literais numéricos, o tipo do literal processado é unsigned long longpara literais integrais ou long doublepara literais de ponto flutuante. (Observação: não há necessidade de tipos integrais assinados porque um literal com prefixo de sinal é analisado como uma expressão que contém o sinal como um operador de prefixo unário e o número sem sinal.) Não há forma de modelo alternativa:

OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _suffix(long double);

OutputType some_variable = 1234_suffix; // Uses the 'unsigned long long' overload.
OutputType another_variable = 3.1416_suffix; // Uses the 'long double' overload.

De acordo com os novos prefixos de string mencionados anteriormente, para literais de string, estes são usados:

OutputType operator "" _ssuffix(const char     * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const wchar_t  * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char32_t * string_values, size_t num_chars);

OutputType some_variable =   "1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable = u8"1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable =  L"1234"_ssuffix; // Uses the 'const wchar_t *'  overload.
OutputType some_variable =  u"1234"_ssuffix; // Uses the 'const char16_t *' overload.
OutputType some_variable =  U"1234"_ssuffix; // Uses the 'const char32_t *' overload.

Não existe um modelo de formulário alternativo. Literais de caracteres são definidos de forma semelhante.

Modelo de memória multithreading

C ++ 11 padroniza o suporte para programação multithread .

Há duas partes envolvidas: um modelo de memória que permite a coexistência de vários threads em um programa e suporte de biblioteca para interação entre os threads. (Consulte a seção deste artigo sobre recursos de threading .)

O modelo de memória define quando vários threads podem acessar o mesmo local de memória e especifica quando as atualizações por um thread se tornam visíveis para outros threads.

Armazenamento local de thread

Em um ambiente multithread, é comum que cada thread tenha algumas variáveis exclusivas . Isso já acontece para as variáveis ​​locais de uma função, mas não para as variáveis ​​globais e estáticas.

Uma nova duração de armazenamento local de thread (além das existentes estáticas , dinâmicas e automáticas ) é indicada pelo especificador de armazenamento thread_local.

Qualquer objeto que possa ter duração de armazenamento estático (ou seja, vida útil abrangendo toda a execução do programa) pode receber a duração local do thread em vez disso. A intenção é que, como qualquer outra variável de duração estática, um objeto thread-local pode ser inicializado usando um construtor e destruído usando um destruidor.

Funções de membro especiais explicitamente padronizadas e excluídas

Em C ++ 03, o compilador fornece, para classes que não as fornecem para si mesmas, um construtor padrão, um construtor de cópia, um operador de atribuição de cópia ( operator=) e um destruidor. O programador pode substituir esses padrões definindo versões personalizadas. C ++ também define vários operadores globais (como operator new) que funcionam em todas as classes, que o programador pode substituir.

No entanto, há muito pouco controle sobre a criação desses padrões. Tornar uma classe inerentemente não copiável, por exemplo, requer declarar um construtor de cópia privada e um operador de atribuição de cópia, e não defini-los. A tentativa de usar essas funções é uma violação da regra de definição única (ODR). Embora uma mensagem de diagnóstico não seja necessária, as violações podem resultar em um erro do vinculador.

No caso do construtor padrão, o compilador não gerará um construtor padrão se uma classe for definida com qualquer construtor. Isso é útil em muitos casos, mas também é útil poder ter construtores especializados e o padrão gerado pelo compilador.

C ++ 11 permite a padronização e exclusão explícitas dessas funções de membro especiais. Por exemplo, este tipo declara explicitamente que está usando o construtor padrão:

struct SomeType
{
    SomeType() = default; //The default constructor is explicitly stated.
    SomeType(OtherType value);
};

Como alternativa, certos recursos podem ser explicitamente desativados. Por exemplo, este tipo não pode ser copiado:

struct NonCopyable
{
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

O = deleteespecificador pode ser usado para proibir a chamada de qualquer função, o que pode ser usado para impedir a chamada de uma função de membro com parâmetros específicos. Por exemplo:

struct NoInt
{
    void f(double i);
    void f(int) = delete;
};

Uma tentativa de chamar f()com um intserá rejeitada pelo compilador, em vez de realizar uma conversão silenciosa para double. Isso pode ser generalizado para impedir a chamada da função com qualquer tipo diferente do doubleseguinte:

struct OnlyDouble
{
    void f(double d);
    template<class T> void f(T) = delete;
};

Modelo long long int

Em C ++ 03, o maior tipo de número inteiro é long int. É garantido ter pelo menos tantos bits utilizáveis ​​quanto int. Isso resultou em um long inttamanho de 64 bits em algumas implementações populares e 32 bits em outras. C ++ 11 adiciona um novo tipo de número inteiro long long intpara resolver esse problema. É garantido que seja pelo menos tão grande quanto a long inte não tenha menos que 64 bits. O tipo foi originalmente introduzido pelo C99 no C padrão, e a maioria dos compiladores C ++ já o suportava como uma extensão.

Asserções estáticas

C ++ 03 fornece dois métodos para testar asserções : a macro asserte a diretiva do pré-processador #error. No entanto, nenhum dos dois é apropriado para uso em modelos: a macro testa a asserção em tempo de execução, enquanto a diretiva do pré-processador testa a asserção durante o pré-processamento, que acontece antes da instanciação dos modelos. Nenhum dos dois é apropriado para testar propriedades que dependem dos parâmetros do modelo.

O novo utilitário apresenta uma nova maneira de testar asserções em tempo de compilação, usando a nova palavra-chave static_assert. A declaração assume esta forma:

static_assert (constant-expression, error-message);

Aqui estão alguns exemplos de como static_assertpodem ser usados:

static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
template<class T>
struct Check
{
    static_assert(sizeof(int) <= sizeof(T), "T is not big enough!");
};
template<class Integral>
Integral foo(Integral x, Integral y)
{
    static_assert(std::is_integral<Integral>::value, "foo() parameter must be an integral type.");
}

Quando a expressão constante é falseo compilador produz uma mensagem de erro. O primeiro exemplo é semelhante à diretiva do pré-processador #error, embora o pré-processador só ofereça suporte a tipos integrais. Em contraste, no segundo exemplo, a asserção é verificada a cada instanciação da classe de modelo Check.

Asserções estáticas também são úteis fora dos modelos. Por exemplo, uma dada implementação de um algoritmo pode depender do tamanho de um long longser maior que um int, algo que o padrão não garante. Essa suposição é válida na maioria dos sistemas e compiladores, mas não em todos.

Permitir sizeoftrabalhar em membros de classes sem um objeto explícito

Em C ++ 03, o sizeofoperador pode ser usado em tipos e objetos. Mas não pode ser usado para fazer isso:

struct SomeType { OtherType member; };

sizeof(SomeType::member); // Does not work with C++03. Okay with C++11

Isso deve retornar o tamanho de OtherType. C ++ 03 não permite isso, portanto, é um erro de compilação. C ++ 11 permite isso. Também é permitido para o alignofoperador introduzido em C ++ 11.

Alinhamento de objetos de controle e consulta

C ++ 11 permite que o alinhamento de variáveis ​​seja consultado e controlado com alignofe alignas.

O alignofoperador pega o tipo e retorna a potência do limite de 2 bytes em que as instâncias de tipo devem ser alocadas (como a std::size_t). Quando fornecido, um tipo de referência alignofretorna o alinhamento do tipo referenciado; para matrizes, ele retorna o alinhamento do tipo de elemento.

O alignasespecificador controla o alinhamento da memória para uma variável. O especificador assume uma constante ou um tipo; quando fornecido, um tipo alignas(T)é uma abreviação de alignas(alignof(T)). Por exemplo, para especificar que uma matriz char deve ser alinhada corretamente para conter um float:

alignas(float) unsigned char c[sizeof(float)]

Permitir implementações de coleta de lixo

Padrões C ++ anteriores fornecidos para coleta de lixo controlada por programador via set_new_handler, mas não deram nenhuma definição de acessibilidade do objeto para o propósito de coleta de lixo automática. C ++ 11 define as condições sob as quais os valores do ponteiro são "derivados com segurança" de outros valores. Uma implementação pode especificar que opera sob estrita segurança de ponteiro , caso em que os ponteiros que não são derivados de acordo com essas regras podem se tornar inválidos.

Atributos

C ++ 11 fornece uma sintaxe padronizada para extensões de compilador / ferramenta para a linguagem. Essas extensões eram tradicionalmente especificadas usando #pragmadiretivas ou palavras-chave específicas do fornecedor (como __attribute__para GNU e __declspecpara Microsoft). Com a nova sintaxe, as informações adicionadas podem ser especificadas na forma de um atributo entre colchetes duplos. Um atributo pode ser aplicado a vários elementos do código-fonte:

int [[attr1]] i [[attr2, attr3]];

[[attr4(arg1, arg2)]] if (cond)
{
    [[vendor::attr5]] return i;
}

No exemplo acima, o atributo attr1se aplica ao tipo de variável i, attr2e attr3aplicam-se a própria variável, attr4aplica-se à ifdeclaração e vendor::attr5aplica-se à instrução de retorno. Em geral (mas com algumas exceções), um atributo especificado para uma entidade nomeada é colocado após o nome e antes da entidade, caso contrário, como mostrado acima, vários atributos podem ser listados dentro de um par de colchetes duplos, argumentos adicionados podem ser fornecidos para um atributo, e os atributos podem ter o escopo definido por namespaces de atributos específicos do fornecedor.

Recomenda-se que os atributos não tenham significado semântico de linguagem e não alterem o sentido de um programa quando ignorados. Os atributos podem ser úteis para fornecer informações que, por exemplo, ajudam o compilador a emitir melhores diagnósticos ou otimizar o código gerado.

O C ++ 11 fornece dois atributos padrão: noreturnpara especificar que uma função não retorna e carries_dependencypara ajudar a otimizar o código multithread, indicando que os argumentos da função ou o valor de retorno carregam uma dependência.

Mudanças na biblioteca padrão C ++

Vários novos recursos foram introduzidos na biblioteca padrão do C ++ 11. Muitos deles poderiam ter sido implementados sob o padrão antigo, mas alguns contam (em maior ou menor grau) nos novos recursos principais do C ++ 11.

Uma grande parte das novas bibliotecas foi definida no documento C ++ Standards Committee's Library Technical Report (chamado TR1), que foi publicado em 2005. Várias implementações completas e parciais de TR1 estão atualmente disponíveis usando o namespace std::tr1. Para C ++ 11, eles foram movidos para o namespace std. No entanto, como os recursos do TR1 foram trazidos para a biblioteca padrão do C ++ 11, eles foram atualizados quando apropriado com os recursos da linguagem do C ++ 11 que não estavam disponíveis na versão inicial do TR1. Além disso, eles podem ter sido aprimorados com recursos que eram possíveis em C ++ 03, mas não faziam parte da especificação TR1 original.

Atualizações para componentes de biblioteca padrão

C ++ 11 oferece uma série de novos recursos de linguagem dos quais os componentes de biblioteca padrão existentes podem se beneficiar. Por exemplo, a maioria dos contêineres de biblioteca padrão pode se beneficiar do suporte ao construtor de movimentação baseado em referência Rvalue, tanto para mover rapidamente contêineres pesados ​​quanto para mover o conteúdo desses contêineres para novos locais de memória. Os componentes da biblioteca padrão foram atualizados com novos recursos de linguagem C ++ 11 quando apropriado. Estes incluem, mas não estão necessariamente limitados a:

  • Referências de Rvalue e o suporte de movimento associado
  • Suporte para a unidade de codificação UTF-16 e tipos de caracteres Unicode da unidade de codificação UTF-32
  • Modelos variáveis ​​(juntamente com referências Rvalue para permitir um encaminhamento perfeito)
  • Expressões constantes de tempo de compilação
  • decltype
  • explicit operadores de conversão
  • Funções declaradas padronizadas ou excluídas

Além disso, muito tempo se passou desde o padrão C ++ anterior. Muito código usando a biblioteca padrão foi escrito. Isso revelou partes das bibliotecas padrão que poderiam ser melhoradas. Entre as muitas áreas de melhoria consideradas estavam os alocadores de biblioteca padrão . Um novo modelo de alocadores baseado em escopo foi incluído no C ++ 11 para complementar o modelo anterior.

Instalações de threading

Embora a linguagem C ++ 03 forneça um modelo de memória que suporte threading, o suporte principal para realmente usar threading vem com a biblioteca padrão C ++ 11.

std::threadÉ fornecida uma classe de thread ( ), que recebe um objeto de função (e uma série opcional de argumentos para passar para ele) para executar na nova thread. É possível fazer com que um encadeamento pare até que outro encadeamento em execução seja concluído, fornecendo suporte para junção de encadeamento por meio da std::thread::join()função de membro. O acesso é fornecido, quando viável, ao (s) objeto (s) de thread nativo (s) subjacente (s) para operações específicas da plataforma pela std::thread::native_handle()função de membro.

Para a sincronização entre as threads, adequadas semáforos ( std::mutex, std::recursive_mutex, etc.), e variáveis de condição ( std::condition_variablee std::condition_variable_any) são adicionados à biblioteca. Eles são acessíveis por meio de bloqueios RAII ( Resource Acquisition Is Initialization ) ( std::lock_guarde std::unique_lock) e algoritmos de bloqueio para facilitar o uso.

Para trabalho de alto desempenho e baixo nível, a comunicação entre threads às vezes é necessária sem a sobrecarga de mutexes. Isso é feito usando operações atômicas em locais de memória. Eles podem, opcionalmente, especificar as restrições mínimas de visibilidade da memória necessárias para uma operação. Barreiras de memória explícita também podem ser usadas para essa finalidade.

A biblioteca de threads do C ++ 11 também inclui futuros e promessas para passar resultados assíncronos entre threads e std::packaged_taskpara encerrar uma chamada de função que pode gerar esse resultado assíncrono. A proposta de futuros foi criticada por carecer de uma forma de combinar futuros e verificar o cumprimento de uma promessa dentro de um conjunto de promessas.

Outros recursos de threading de alto nível, como pools de thread , foram devolvidos para um futuro relatório técnico C ++ . Eles não fazem parte do C ++ 11, mas sua eventual implementação deve ser construída inteiramente sobre os recursos da biblioteca de threads.

A nova std::asyncinstalação oferece um método conveniente de executar tarefas e vinculá-las a um std::future. O usuário pode escolher se a tarefa deve ser executada de forma assíncrona em um thread separado ou de forma síncrona em um thread que aguarda o valor. Por padrão, a implementação pode escolher, o que fornece uma maneira fácil de tirar proveito da simultaneidade de hardware sem excesso de assinaturas e fornece algumas das vantagens de um pool de threads para usos simples.

tipos tupla

Tuplas são coleções compostas por objetos heterogêneos de dimensões pré-arranjadas. Uma tupla pode ser considerada uma generalização das variáveis ​​de membro de uma estrutura.

A versão C ++ 11 do tipo de tupla TR1 se beneficiou dos recursos do C ++ 11, como modelos variados . Para implementar razoavelmente, a versão TR1 exigia um número máximo definido pela implementação de tipos contidos e truques de macro substanciais. Em contraste, a implementação da versão C ++ 11 não requer um número máximo de tipos definido pela implementação explícita. Embora os compiladores tenham uma profundidade de recursão máxima interna para a instanciação do modelo (o que é normal), a versão C ++ 11 das tuplas não expõe esse valor ao usuário.

Usando modelos variados , a declaração da classe de tupla é a seguinte:

template <class ...Types> class tuple;

Um exemplo de definição e uso do tipo de tupla:

typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");

lengthy = std::get<0>(proof);  // Assign to 'lengthy' the value 18.
std::get<3>(proof) = " Beautiful!";  // Modify the tuple’s fourth element.

É possível criar a tupla proofsem definir seu conteúdo, mas apenas se os tipos dos elementos da tupla possuírem construtores padrão. Além disso, é possível atribuir uma tupla a outra tupla: se os tipos das duas tuplas são iguais, cada tipo de elemento deve possuir um construtor de cópia; caso contrário, cada tipo de elemento da tupla do lado direito deve ser conversível para aquele do tipo de elemento correspondente da tupla do lado esquerdo ou que o tipo de elemento correspondente da tupla do lado esquerdo tenha um construtor adequado.

typedef std::tuple <int , double, string       > tuple_1 t1;
typedef std::tuple <char, short , const char * > tuple_2 t2 ('X', 2, "Hola!");
t1 = t2; // Ok, first two elements can be converted,
         // the third one can be constructed from a 'const char *'.

Assim como std::make_pairpara std::pair, existe std::make_tuplepara criar std::tuples automaticamente usando dedução de tipo e autoajuda a declarar tal tupla. std::tiecria tuplas de referências lvalue para ajudar a desempacotar tuplas. std::ignoretambém ajuda aqui. Veja o exemplo:

auto record = std::make_tuple("Hari Ram", "New Delhi", 3.5, 'A');
std::string name ; float gpa ; char grade ;
std::tie(name, std::ignore, gpa, grade) = record ; // std::ignore helps drop the place name
std::cout << name << ' ' << gpa << ' ' << grade << std::endl ;

Operadores relacionais estão disponíveis (entre tuplas com o mesmo número de elementos), e duas expressões estão disponíveis para verificar as características de uma tupla (somente durante a compilação):

  • std::tuple_size<T>::valueretorna o número de elementos na tupla T,
  • std::tuple_element<I, T>::typeretorna o tipo do número Ido objeto da tupla T.

Tabelas de hash

Incluir tabelas hash (contêineres associativos não ordenados) na biblioteca padrão C ++ é uma das solicitações mais recorrentes. Não foi adotado no C ++ 03 devido apenas a restrições de tempo. Embora as tabelas de hash sejam menos eficientes do que uma árvore balanceada no pior caso (na presença de muitas colisões), elas têm um desempenho melhor em muitos aplicativos reais.

As colisões são geridas apenas por encadeamento linear porque a comissão não considerou oportuno padronizar soluções de endereçamento aberto que introduzem uma série de problemas intrínsecos (sobretudo quando se admite o apagamento de elementos). Para evitar conflitos de nomes com bibliotecas não padrão que desenvolveram suas próprias implementações de hash table, o prefixo “não ordenado” foi usado em vez de “hash”.

A nova biblioteca tem quatro tipos de tabelas de hash, diferenciadas por aceitarem ou não elementos com a mesma chave (chaves exclusivas ou chaves equivalentes) e se mapeiam cada chave para um valor associado. Eles correspondem aos quatro contêineres associativos existentes com base na árvore de pesquisa binária , com um prefixo não ordenado .

Tipo de tabela de hash Valores associados Chaves equivalentes
std::unordered_set Não Não
std::unordered_multiset Não sim
std::unordered_map sim Não
std::unordered_multimap sim sim

As novas classes cumprir todos os requisitos de uma classe de contêiner , e têm todos os métodos necessários para elementos de acesso: insert, erase, begin, end.

Este novo recurso não precisava de nenhuma extensão central da linguagem C ++ (embora as implementações aproveitem os vários recursos da linguagem C ++ 11), apenas uma pequena extensão do cabeçalho <functional>e a introdução de cabeçalhos <unordered_set>e <unordered_map>. Nenhuma outra alteração em nenhuma classe padrão existente foi necessária e não depende de nenhuma outra extensão da biblioteca padrão.

Expressões regulares

A nova biblioteca, definida no novo cabeçalho <regex>, é composta por algumas novas classes:

  • expressões regulares são representadas por instância da classe de modelo std::regex;
  • as ocorrências são representadas por instância da classe de modelo std::match_results.

A função std::regex_searché usada para pesquisar, enquanto para 'pesquisar e substituir' a função std::regex_replaceé usada que retorna uma nova string. Os algoritmos std::regex_searche std::regex_replacepegam uma expressão regular e uma string e gravam as ocorrências encontradas na estrutura std::match_results.

Aqui está um exemplo do uso de std::match_results:

const char *reg_esp = "[ ,.\\t\\n;:]"; // List of separator characters.

// this can be done using raw string literals:
// const char *reg_esp = R"([ ,.\t\n;:])";

std::regex rgx(reg_esp); // 'regex' is an instance of the template class
                         // 'basic_regex' with argument of type 'char'.
std::cmatch match; // 'cmatch' is an instance of the template class
                   // 'match_results' with argument of type 'const char *'.
const char *target = "Unseen University - Ankh-Morpork";

// Identifies all words of 'target' separated by characters of 'reg_esp'.
if (std::regex_search(target, match, rgx))
{
    // If words separated by specified characters are present.

    const size_t n = match.size();
    for (size_t a = 0; a < n; a++)
    {
        std::string str (match[a].first, match[a].second);
        std::cout << str << "\n";
    }
}

Observe o uso de barras invertidas duplas , porque C ++ usa barra invertida como um caractere de escape. O recurso de string bruta do C ++ 11 pode ser usado para evitar o problema.

A biblioteca <regex>não requer alteração de qualquer cabeçalho existente (embora os use quando apropriado) nem uma extensão da linguagem central. No POSIX C, as expressões regulares também estão disponíveis na biblioteca C POSIX # regex.h .

Dicas inteligentes de uso geral

C ++ 11 fornece std::unique_ptr, e melhorias para std::shared_ptre std::weak_ptrde TR1. std::auto_ptrestá obsoleto.

Instalação de número aleatório extensível

A biblioteca padrão C fornece a capacidade de gerar números pseudo-aleatórios por meio da função rand. No entanto, o algoritmo é delegado inteiramente ao fornecedor da biblioteca. C ++ herdou essa funcionalidade sem alterações, mas C ++ 11 fornece um novo método para gerar números pseudoaleatórios.

A funcionalidade de número aleatório do C ++ 11 é dividida em duas partes: um mecanismo gerador que contém o estado do gerador de número aleatório e produz os números pseudo-aleatórios; e uma distribuição, que determina o alcance e a distribuição matemática do resultado. Esses dois são combinados para formar um objeto gerador de número aleatório.

Ao contrário do padrão C rand, o mecanismo C ++ 11 virá com três algoritmos de mecanismo gerador de base:

C ++ 11 também fornece uma série de distribuições padrão:

O gerador e as distribuições são combinados como neste exemplo:

#include <random>
#include <functional>

std::uniform_int_distribution<int> distribution(0, 99);
std::mt19937 engine; // Mersenne twister MT19937
auto generator = std::bind(distribution, engine);
int random = generator(); // Generate a uniform integral variate between 0 and 99.
int random2 = distribution(engine); // Generate another sample directly using the distribution and the engine objects.

Referência de invólucro

Uma referência de wrapper é obtida de uma instância da classe de modelo reference_wrapper. As referências do wrapper são semelhantes às referências normais (' &') da linguagem C ++. Para obter uma referência de wrapper de qualquer objeto, o modelo de função refé usado (para uma referência constante crefé usada).

As referências de wrapper são úteis acima de tudo para modelos de função, onde referências a parâmetros em vez de cópias são necessárias:

// This function will obtain a reference to the parameter 'r' and increment it.
void func (int &r)  { r++; }

// Template function.
template<class F, class P> void g (F f, P t)  { f(t); }

int main()
{
    int i = 0;
    g (func, i); // 'g<void (int &r), int>' is instantiated
                 // then 'i' will not be modified.
    std::cout << i << std::endl; // Output -> 0

    g (func, std::ref(i)); // 'g<void(int &r),reference_wrapper<int>>' is instantiated
                           // then 'i' will be modified.
    std::cout << i << std::endl; // Output -> 1
}

Este novo utilitário foi adicionado ao <utility>cabeçalho existente e não precisava de mais extensões da linguagem C ++.

Wrappers polimórficos para objetos de função

Wrappers polimórficos para objetos de função são semelhantes a ponteiros de função em semântica e sintaxe, mas são menos restritos e podem se referir indiscriminadamente a qualquer coisa que possa ser chamada (ponteiros de função, ponteiros de função de membro ou functores) cujos argumentos são compatíveis com os do wrapper .

Um exemplo pode esclarecer suas características:

std::function<int (int, int)> func; // Wrapper creation using
                                    // template class 'function'.
std::plus<int> add; // 'plus' is declared as 'template<class T> T plus( T, T ) ;'
                    // then 'add' is type 'int add( int x, int y )'.
func = add;  // OK - Parameters and return types are the same.

int a = func (1, 2); // NOTE: if the wrapper 'func' does not refer to any function,
                     // the exception 'std::bad_function_call' is thrown.

std::function<bool (short, short)> func2 ;
if (!func2)
{
    // True because 'func2' has not yet been assigned a function.

    bool adjacent(long x, long y);
    func2 = &adjacent; // OK - Parameters and return types are convertible.

    struct Test
    {
        bool operator()(short x, short y);
    };
    Test car;
    func = std::ref(car); // 'std::ref' is a template function that returns the wrapper
                          // of member function 'operator()' of struct 'car'.
}
func = func2; // OK - Parameters and return types are convertible.

A classe do template functionfoi definida dentro do cabeçalho <functional>, sem a necessidade de nenhuma alteração na linguagem C ++.

Características de tipo para metaprogramação

A metaprogramação consiste na criação de um programa que cria ou modifica outro programa (ou a si mesmo). Isso pode acontecer durante a compilação ou durante a execução. O Comitê de Padrões C ++ decidiu introduzir uma biblioteca que permite a metaprogramação durante a compilação por meio de modelos.

Aqui está um exemplo de um metaprograma, usando o padrão C ++ 03: uma recursão de instâncias de modelo para calcular expoentes inteiros:

template<int B, int N>
struct Pow
{
    // recursive call and recombination.
    enum{ value = B*Pow<B, N-1>::value };
};

template< int B >
struct Pow<B, 0>
{
    // ''N == 0'' condition of termination.
    enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;

Muitos algoritmos podem operar em diferentes tipos de dados; Os modelos de C ++ oferecem suporte à programação genérica e tornam o código mais compacto e útil. No entanto, é comum que algoritmos precisem de informações sobre os tipos de dados usados. Essas informações podem ser extraídas durante a instanciação de uma classe de modelo usando características de tipo .

Traços de tipo podem identificar a categoria de um objeto e todas as características de uma classe (ou de uma estrutura). Eles são definidos no novo cabeçalho <type_traits>.

No próximo exemplo existe a função template 'elaborate' que, dependendo dos tipos de dados fornecidos, irá instanciar um dos dois algoritmos propostos ( algorithm.do_it).

// First way of operating.
template< bool B > struct Algorithm
{
    template<class T1, class T2> static int do_it (T1 &, T2 &)  { /*...*/ }
};

// Second way of operating.
template<> struct Algorithm<true>
{
    template<class T1, class T2> static int do_it (T1, T2)  { /*...*/ }
};

// Instantiating 'elaborate' will automatically instantiate the correct way to operate.
template<class T1, class T2>
int elaborate (T1 A, T2 B)
{
    // Use the second way only if 'T1' is an integer and if 'T2' is
    // in floating point, otherwise use the first way.
    return Algorithm<std::is_integral<T1>::value && std::is_floating_point<T2>::value>::do_it( A, B ) ;
}

Por meio de características de tipo , definidas no cabeçalho <type_traits>, também é possível criar operações de transformação de tipo ( static_caste const_castsão insuficientes dentro de um template).

Esse tipo de programação produz código elegante e conciso; entretanto o ponto fraco dessas técnicas é a depuração: desconfortável durante a compilação e muito difícil durante a execução do programa.

Método uniforme para calcular o tipo de retorno de objetos de função

Determinar o tipo de retorno de um objeto de função de modelo em tempo de compilação não é intuitivo, especialmente se o valor de retorno depende dos parâmetros da função. Como um exemplo:

struct Clear
{
    int    operator()(int) const;    // The parameter type is
    double operator()(double) const; // equal to the return type.
};

template <class Obj>
class Calculus
{
public:
    template<class Arg> Arg operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

Instanciando o modelo de classe Calculus<Clear>, o objeto de função de calculusterá sempre o mesmo tipo de retorno que o objeto de função de Clear. No entanto, dada a aula Confusedabaixo:

struct Confused
{
    double operator()(int) const;     // The parameter type is not
    int    operator()(double) const;  // equal to the return type.
};

A tentativa de instanciar Calculus<Confused>fará com que o tipo de retorno de Calculusnão seja igual ao de classe Confused. O compilador pode gerar avisos sobre a conversão de intpara doublee vice-versa.

TR1 introduz, e C ++ 11 adota, a classe de modelo std::result_ofque permite determinar e usar o tipo de retorno de um objeto de função para cada declaração. O objeto CalculusVer2usa o std::result_ofobjeto para derivar o tipo de retorno do objeto de função:

template< class Obj >
class CalculusVer2
{
public:
    template<class Arg>
    typename std::result_of<Obj(Arg)>::type operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

Desta forma, em instâncias de objeto de função de CalculusVer2<Confused>não há conversões, avisos ou erros.

A única mudança da versão TR1 de std::result_ofé que a versão TR1 permitiu que uma implementação falhasse em determinar o tipo de resultado de uma chamada de função. Devido a alterações no suporte decltypedo C ++, a versão C ++ 11 do std::result_ofnão precisa mais desses casos especiais; implementações são necessárias para calcular um tipo em todos os casos.

Compatibilidade C aprimorada

Para compatibilidade com C , a partir de C99, foram adicionados:

  • Pré-processador:
    • macros variadic ,
    • concatenação de literais de string estreitos / largos adjacentes,
    • _Pragma()- equivalente a #pragma.
  • long long - tipo inteiro com pelo menos 64 bits de comprimento.
  • __func__ - macro avaliando o nome da função em que está.
  • Cabeçalhos:
    • cstdbool( stdbool.h),
    • cstdint( stdint.h),
    • cinttypes( inttypes.h)

Recursos originalmente planejados, mas removidos ou não incluídos

Rumo a um TR separado:

  • Módulos
  • Tipos decimais
  • Funções especiais matemáticas

Postergado:

  • Conceitos
  • Suporte de coleta de lixo mais completo ou obrigatório
  • Reflexão
  • Âmbitos de macro

Recursos removidos ou obsoletos

O termo ponto de sequência foi removido, sendo substituído pela especificação de que uma das operações é sequenciada antes da outra ou que duas operações não são sequenciadas.

O antigo uso da palavra-chave exportfoi removido. A própria palavra-chave permanece, sendo reservada para uso futuro potencial.

As especificações de exceção dinâmica foram descontinuadas. A especificação de tempo de compilação de funções que não lançam exceções está disponível com a noexceptpalavra - chave, o que é útil para otimização.

std::auto_ptrestá obsoleto, tendo sido substituído por std::unique_ptr.

Classes de base de objeto de função ( std::unary_function, std::binary_function), adaptadores para ponteiros para funções e adaptadores para ponteiros para membros e classes de fichário foram todos descontinuados.

Veja também

Referências

links externos