C ++ 20 aprovado! O que esperar e o que preparar para os desenvolvedores em C ++ 23

Outro dia, em Praga, ocorreu uma reunião do comitê internacional de padronização C ++. And-and-and-and ...



C ++ 20 está pronto! Resta colocar um selo da ISO, mas este é um passo puramente formal com o qual não deve haver problemas.

Parabéns a todos por este maravilhoso evento! Conceitos, Corotinas, Módulos, Intervalos, formato std ::, constexpr new e constexpr algoritmos + vetor + string, datetime, jthread, span, bit_cast e muitas outras pequenas e grandes inovações.

O que eles conseguiram adicionar e corrigir no último momento, o que eles propuseram quebrar e o que todo mundo quer ver no C ++ 23 - sobre tudo isso em detalhes.

Truques de C para C ++ 20


Recentemente, existe uma tradição de assustar desenvolvedores iniciantes com comportamento indefinido (UB) em C ++. É hora de mudar isso!

Aqui, por exemplo, esse código é absolutamente válido para C:

struct X { int a, b; };

X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

Mas em C ++ havia grandes problemas com isso. C opera com bytes e C ++ trabalha com objetos. Mas o objeto tem uma vida útil e, antes de C ++ 20, o início da vida do objeto era considerado a partir da chamada nova.

O comitê está seriamente preocupado com o trabalho de baixo nível com bytes e estruturas simples. Eles adotaram melhorias que dizem que um determinado conjunto de funções (memcpy, memmove, malloc, align_alloc, calloc, realloc, bit_cast) inicia a vida útil do objeto. Agora, a maioria dos truques C de baixo nível tem garantia de funcionar em C ++.

bool tornou-se mais confiável em C ++ 20


Adivinha qual é o erro:

template <typename T, size_t N>
auto count_unique(const std::array<T, N>& v) {
    return std::unordered_set<T>{v.begin(), v.end()}.size();
}

Responda
T bool v.begin() v.end() (, libc++ libstdc++) — count_unique 1.

- , std::unordered_set<bool>{v.begin(), v.end()} bool std::unordered_set<bool>{true, true}.

As transformações no bool são agora consideradas transformações restritivas. Isso nos permitiu encontrar problemas em muitas bases de código (mais exemplos na própria sentença P1957 ).

Conceitos C ++ 20 mais rápidos


Até recentemente, você poderia escrever um conceito como este:

template <class T>
concept Reservable = requires(T v) {
    v.reserve(int{});
};

E surpreenda-se ao retornar resultados diferentes:

struct Test;

static_assert(!Reservable<Test>);

struct Test {
    void reserve(int);
};

static_assert(Reservable<Test>);

Na última vez, enviamos um comentário do país: "Torne impossível o uso de tipos incompletos em conceitos, pois, caso contrário, você recebe várias violações de ODR". Nosso comentário foi rejeitado, mas obtivemos parcialmente o resultado desejado agora com a proposta P2104.

Como bônus, obtemos uma compilação mais rápida, já que os compiladores agora têm o direito de armazenar em cache os resultados da aplicação de conceitos aos tipos.

Edições menores em C ++ 20


  • As faixas têm o método ssize.
  • O vínculo interno das entidades não é mais visível ao instanciar entidades de vínculo do módulo (o compilador informará o que está errado no estágio de compilação).
  • Torcemos as regras dos módulos para que fosse mais fácil qualquer ferramenta trabalhar com eles.

E vamos quebrar tudo em C ++ 23 ou C ++ 26?


O debate prolongado desencadeou uma proposta para uma Interface Binária de Aplicativo (ABI, não confunda com a API). Questões interessantes foram levantadas:

1. Podemos alterar completamente a ABI no C ++ 23 e obter um ganho de desempenho de 5 a 10%.


Além disso, todas as antigas bibliotecas C ++ precisarão ser reconstruídas; elas não poderão vincular as bibliotecas à nova ABI. Você não pode usar bibliotecas criadas por versões anteriores do C ++ em um projeto C ++ 23.

Bem, é claro, sempre haverá software comercial antigo que ninguém remontará, mas que arrastará sua biblioteca padrão (sim, videogames, estou falando de você!).

Com uma ligeira margem de voto, a ABI decidiu não interromper o C ++ 23.

2. Vamos dar aos usuários uma garantia de que tentaremos não quebrar / alterar a ABI.


E então eles decidiram não dar uma garantia. Diferentes fornecedores têm planos diferentes para suas plataformas e, às vezes, podem se dar ao luxo de quebrar a ABI, geralmente sem prejudicar os usuários.

E vamos adicionar em qualquer lugar, exceto?


Historicamente, nas funções padrão com pré-condições não são marcadas como nenhuma exceção, mesmo que nunca lançem exceções. Aqui, por exemplo, operator -> have std :: optional:

constexpr const T* operator->() const;
constexpr T* operator->();
    Requires: *this contains a value.
    Returns: val.
    Throws: Nothing.

Ele não joga nada, mas, em vez de noexcept, diz em palavras que "joga: nada", porque existe uma pré-condição "* isso contém um valor".

Usuários com noexcept serão mais claros. Uma boa ideia no P1656 !



Não!

Existe todo um subgrupo SG21: Contratos, que cria um mecanismo comum para verificar contratos (pré e pós-condições). Os manipuladores de contrato podem lançar uma exceção, se uma exceção for lançada da função noexcept, ela será std :: terminate e o aplicativo falhará. Se você inserir uma muleta especial que, para contratos, as exceções possam sair de uma função noexcept ... tudo será quebrado de qualquer maneira, os traços de tipo serão guiados pela presença de noexcept e eles começarão a mentir para você, uma função marcada com noexcept com uma pré-condição lançará uma exceção.

Mas este não é o maior problema. Existem bifurcações de bibliotecas padrão, que já agora, em alguns casos, inserem explicitamente verificações de pré-condição. Por exemplo, você tem um projeto crítico, cuja disponibilidade deve ser maximizada. Você usa uma bifurcação semelhante e, se alguém chama subitamente std :: vector :: back () para um vetor vazio, lança uma exceção, que é processada mais alto no código e o fallback começa a ser usado. Com as edições do P1656, essa biblioteca não pode mais ser considerada padrão.

E isso não é todos os problemas! .. Além de a biblioteca padrão trazer efeitos positivos na forma de uma diminuição no tamanho dos arquivos binários ou mais desempenho, a mudança não quebrará o código de pelo menos duas empresas, além disso, destrói uma das maneiras. uso de contratos ... também a proposta foi aprovada em dois subgrupos.

Méritos do RG21


Como sempre, trabalhamos em vários subgrupos, compartilhamos experiências de implementação, apresentamos propostas cujos autores não puderam vir.

Uma das idéias mais marcantes que tivemos a sorte de apresentar foi a da Anton Zhilin P2025 Elision de cópia garantida para objetos de retorno nomeados . Sua implementação permitirá criar funções de fábrica para objetos sem copiar e mover construtores. De fato, esse é um movimento destrutivo, que existe secretamente no padrão desde meados dos anos 90 e foi especificamente proibido por regras de idioma individuais.

Nós arrastamos a idéia com sucesso pelas instâncias EWG-I e EWG, graças à excelente elaboração da idéia pelo próprio autor. O estágio do CWG permaneceu e, após algumas reuniões, há todas as chances de ver as palavras necessárias no padrão e as primeiras implementações nos compiladores.

Além dessa idéia, arrastamos a idéia de P1990R0: Add operator [] para std :: initializer_list via LEWG-I, obtivemos um feedback útil sobre P1944R0: constexpr <cstring> e <cwchar> . As duas idéias de Daniil Goncharov têm todas as chances de estar em C ++ 23.

No campo std :: hash, uma falha inesperada nos esperava. A discussão de p1406r1: Adicione mais especializações std :: hash de repente se transformou em uma discussão de casos de fronteira degenerados e as possibilidades de C ++ 2 * distante. Como resultado, o comitê decidiu não mudar nada.

Com SG6 e Numbers não cresceram juntos. As principais discussões do SG6 se cruzaram com as discussões da ABI, razão pela qual o quorum no SG6 não se acumulou. Devido a isso, p1889: C ++ Numerics Work In Progress , P2010: Remova os operadores iostream do P1889 eP1890: Questões numéricas do C ++ em andamento não foram discutidas.

Planos C ++ 23


Desde o início do desenvolvimento do C ++ 20, o comitê começou a agir de acordo com o planejado. Ou seja, identificar várias idéias interessantes importantes para o próximo padrão, após o qual, em todas as reuniões subsequentes, não deve considerar propostas sobre outros tópicos, se nem todas foram discutidas no principal.

Para o C ++ 23, esse plano foi aprovado em Praga. As principais prioridades do C ++ 23:

  1. Suporte Corutin na biblioteca padrão
  2. Converter biblioteca padrão em módulos
  3. Executores
  4. Trabalho em rede

No primeiro parágrafo, todos serão guiados pela biblioteca CppCoro . Portanto, se você já deseja usar corotinas C ++ 20, comece usando esta biblioteca.

Com os módulos, o segundo ponto, você só precisa se sentar e fazê-lo, não são esperadas dificuldades especiais.

Mas Executors é um problema. Seu design não é óbvio, nem todos os casos de usuário são cobertos; em sua forma atual, eles não foram usados ​​por ninguém, e o design ainda não foi aprovado.

O comitê também concordou em priorizar propostas nas seguintes áreas:

  • Reflexão
  • Correspondência de padrões
  • Contratos

Em vez de totais


O C ++ 20 está pronto, é hora de trabalhar no C ++ 23! A próxima reunião do Comitê será no verão; portanto, se você tiver uma idéia decente do novo padrão - compartilhe-o no stdcpp.ru e no ProCxx, com bate-papo com telegrama .

Bem, todo mundo que quiser conversar com representantes do comitê ao vivo - veja as reuniões e conferências em C ++ *:


* Life hack: você não precisa pagar por um ingresso em conferência se for um orador.

All Articles