Shopping cart

Subtotal $0.00

View cartCheckout

Building better devs

TnewsTnews
Programação

C++26 Aprovado: 7 Features Que Vão Mudar Como Você Programa

Código de programação C++ em tela com syntax highlighting representando as novas features do C++26
Email : 39

O C++ acabou de mudar para sempre

Eu acompanho o comitê ISO do C++ há mais de uma década. Já vi promessas grandes que viraram decepções (alguém lembra dos Concepts no C++17 que não rolaram?), já vi features sendo adiadas por ciclos inteiros, e já vi aquela sensação de “mais do mesmo” em algumas releases. Mas o que aconteceu em Londres, no final de março de 2026, é diferente.

O C++26 foi oficialmente finalizado. E não é uma atualização incremental. Herb Sutter — que dispensa apresentações — comparou o impacto ao que o C++11 fez lá atrás. Nas palavras dele: “uma segunda onda de modernização, com uma linguagem e estilo visivelmente renovados.”

Exagero? Eu acharia que sim, se não fossem as quatro features que entraram juntas nessa versão.

Reflection: o C++ entregou as chaves da própria máquina

Vou ser direto: reflection é a maior adição ao C++ desde templates. E eu não estou exagerando.

Até agora, se você queria fazer introspecção em tempo de compilação — saber os membros de uma struct, gerar código baseado na estrutura de um tipo, criar serializers automáticos — você dependia de macros horríveis, template metaprogramming que parecia hieróglifo, ou ferramentas externas de code generation.

O C++26 resolve isso com um operador novo: ^^ (carinhosamente apelidado de “cat ears operator”). Ele transforma qualquer entidade do programa em um valor do tipo std::meta::info, que você pode manipular em tempo de compilação como qualquer outro valor.

Para trazer de volta ao mundo “real” do C++, você usa o splicer: [: ... :].

A lógica é simples: ^^ para entrar no mundo da reflexão, [: :] para sair.

struct Point {
    int x;
    int y;
};

Point p{24, 42};

// Acessar o segundo membro por reflexão
constexpr auto member_y = std::meta::nonstatic_data_members_of(^^Point)[1];
std::cout << p.[:member_y:] << '\n'; // imprime: 42

Parece simples, né? Mas pensa no que isso habilita:

Caso de UsoAntes do C++26Com Reflection
Serialização JSON automáticaMacros manuais ou libs como nlohmann + boilerplateIteração sobre membros em compile-time
ORM / mapeamento SQLCode generators externosGeração de queries em consteval
Debug printingOverload manual de operator<<Auto-geração para qualquer struct
Enum para stringTabelas manuais ou magic_enum (hack)std::meta::identifier_of(^^MyEnum)
Reflection em frameworks (Qt, etc.)MOC (meta-object compiler)Nativo na linguagem

O Qt já está experimentando. A equipe publicou um post mostrando como QRangeModel pode usar reflection do C++26 para eliminar o MOC em cenários específicos. Bloomberg open-sourced um fork do Clang com suporte experimental. O EDG também tem implementação disponível no Compiler Explorer.

Herb Sutter resumiu bem: “C++ cruzou um Rubicão. Entregamos as chaves da própria maquinaria.”

O template for que muda tudo

Uma das sintaxes mais aguardadas é o template for, que permite iterar sobre membros refletidos de forma natural:

template <typename T>
void print_fields(const T& obj) {
    template for (constexpr auto member :
                  std::meta::nonstatic_data_members_of(^^T)) {
        std::cout << std::meta::identifier_of(member)
                  << " = " << obj.[:member:] << '\n';
    }
}

Chama print_fields com qualquer struct e ele imprime todos os campos automaticamente. Sem macro. Sem boilerplate. Sem template metaprogramming obscuro.

Contracts: o fim do assert como você conhece

Se reflection é a feature mais “wow”, contracts é a mais importante para a saúde do ecossistema.

A ideia não é nova — contracts estavam previstos para o C++20, foram removidos de última hora por falta de consenso, e passaram seis anos sendo redesenhados. O resultado final (proposta P2900) foi aprovado com 114 votos a favor, 12 contra e 3 abstenções.

A sintaxe final usa três construtos:

int divide(int a, int b)
    pre(b != 0)                    // precondição: b não pode ser zero
    post(result : result * b == a) // pós-condição: resultado é consistente
{
    contract_assert(a > 0);        // asserção no corpo da função
    return a / b;
}

pre verifica antes da chamada. post verifica depois (e dá nome ao retorno via result : expressão). contract_assert substitui o velho assert() da <cassert>.

Por que isso importa tanto?

Porque C++ é usado em sistemas onde bugs matam — literalmente. Aviação, automotivo, dispositivos médicos, infraestrutura financeira. Contracts dão uma forma padronizada de expressar invariantes que podem ser:

  • Ignoradas em builds de release (zero overhead)
  • Observadas (logam a violação mas continuam)
  • Enforced (abortam na violação)
  • Quick-enforced (abortam sem cleanup, para ambientes safety-critical)

Isso não é unit test. É documentação executável no nível da interface. O compilador sabe o que a função espera e o que ela promete.

// Antes: comentário que ninguém lê
// ATENÇÃO: vec não pode estar vazio!
double average(const std::vector<double>& vec) {
    return std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size();
}

// Depois: contrato que o compilador pode verificar
double average(const std::vector<double>& vec)
    pre(!vec.empty())
    post(r : std::isfinite(r))
{
    return std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size();
}

A diferença é que no segundo caso, se alguém chamar average({}), o comportamento é definido e configurável — não é UB silencioso que corrompe memória três funções depois.

std::execution: async sem lágrimas

Pergunta honesta: quantas vezes você já escreveu código assíncrono em C++ e sentiu que estava lutando contra a linguagem?

std::future é uma piada — bloqueia na hora de pegar o resultado. std::async tem semântica tão confusa que até os membros do comitê recomendam não usar. E se você quer algo real, acaba dependendo de Boost.Asio, libunifex, ou escrevendo seu próprio thread pool com condition variables e rezando para não ter data race.

O std::execution, baseado no modelo Sender/Receiver (proposta P2300), é a resposta que a comunidade esperava há uma década. Ele fornece um framework padronizado para:

  • Compor operações assíncronas de forma estruturada
  • Garantir ausência de data races por construção (não por convenção)
  • Funcionar com qualquer executor (thread pool, GPU, rede, io_uring)
  • Suportar cancelamento, timeouts e error handling de forma uniforme

O conceito central é elegante: um sender representa uma unidade de trabalho assíncrono que envia seu resultado para um receiver. Você compõe senders como se fossem pipes.

#include <execution>
namespace ex = std::execution;

// Compor operações assíncronas
auto work = ex::just(42)                          // começa com valor 42
          | ex::then([](int x) { return x * 2; }) // transforma
          | ex::then([](int x) { return std::to_string(x); }); // converte

// Executar em um scheduler
ex::sync_wait(ex::on(my_thread_pool.get_scheduler(), work));

O que torna isso diferente de outras tentativas? A composabilidade. Senders são valores. Você pode guardá-los em variáveis, passá-los para funções, compor cadeias complexas de operações — tudo type-safe e sem callbacks hell.

// Exemplo mais realista: fetch HTTP + parse + salvar
auto pipeline = ex::just(url)
    | ex::then(http_fetch)          // IO-bound: roda no IO scheduler
    | ex::then(parse_json)          // CPU-bound: roda no thread pool
    | ex::let_value([](auto data) {
        return ex::just(data)
             | ex::then(save_to_db); // IO-bound de novo
    });

// Tudo isso é lazy — nada executa até:
ex::sync_wait(ex::on(scheduler, pipeline));

A Citadel — sim, o hedge fund — já usa std::execution do C++26 em produção para uma classe inteira de ativos e como base da infraestrutura de mensageria deles. Quando Wall Street adota antes do standard ser publicado, você sabe que a coisa é séria.

O C++26 ainda expandiu o <execution> com:

  • std::execution::task — coroutine type integrado ao framework sender/receiver
  • Async scopes — RAII para recursos assíncronos (porque ninguém quer leak de tasks)
  • Parallel scheduler — contexto de execução avançado para thread pools

Pra quem vem do mundo JavaScript, pense em senders como Promises — mas type-safe, sem garbage collector, e com controle total sobre onde e como cada step executa.

Memory Safety: o C++ ouviu o Rust (finalmente)

Esse é o elefante na sala. A Casa Branca pediu para pararem de usar C/C++ em software crítico. A NSA recomendou linguagens memory-safe. O Rust virou o queridinho. E o comitê do C++ respondeu.

Não com uma solução mágica — mas com duas mudanças pragmáticas que já mostraram resultado em produção.

Fim do UB em variáveis não inicializadas

Até o C++23, ler uma variável local não inicializada era comportamento indefinido. O compilador podia fazer literalmente qualquer coisa — incluindo “otimizar” seu código de formas que pareciam funcionar em debug mas quebravam em release.

No C++26, isso agora tem comportamento definido. O valor pode ser indeterminado, mas o programa não entra em UB.

Parece pouca coisa? A Google estima que essa classe de bug sozinha era responsável por centenas de vulnerabilidades por ano nos produtos deles.

Hardened Standard Library

A segunda mudança é a biblioteca padrão “endurecida” — bounds checking para vector, span, string, string_view e tipos similares.

std::vector<int> v = {1, 2, 3};
int x = v[5]; // C++23: UB silencioso. C++26 hardened: abort

O dado mais impressionante vem do deploy real. Google e Apple implementaram versões hardened das suas standard libraries e mediram:

  • ~0.3% de overhead de performance
  • 1.000 a 2.000 bugs prevenidos por ano

Zero-vírgula-três por cento. É o preço de um café por mês para evitar milhares de potenciais vulnerabilidades.

Isso resolve todos os problemas de memory safety do C++? Claro que não. Dangling pointers, use-after-free, double-free — tudo isso ainda existe. Mas é um passo pragmático e mensurável. E diferente de “reescreva tudo em Rust”, é algo que pode ser adotado incrementalmente em codebases de milhões de linhas que já existem.

A mensagem do comitê é clara: não vamos abandonar o legado, mas vamos torná-lo mais seguro. Feature por feature, release por release.

O que mais entrou (e o que ficou de fora)

Além dos “Fab Four”, o C++26 trouxe:

FeatureO que faz
SIMD Types (Parallelism TS 2)Tipos vetoriais portáveis para SIMD — finalmente padronizado
std::embedEmbarca arquivos binários no executável em tempo de compilação
Parallel RangesAlgoritmos paralelos para a biblioteca Ranges
Quantities and UnitsTipos para grandezas físicas (metros, segundos, etc.) — previne erros como o do Mars Climate Orbiter

E o que não entrou? Trivial Relocatability foi removida na última hora por causa de um “showstopper bug”. É aquele tipo de decisão que dói, mas mostra maturidade do comitê — melhor adiar do que shipar quebrado.

Pattern matching, que muitos esperavam, também ficou de fora. Provavelmente vai para o C++29. Se você é fã de Rust ou Haskell e queria match no C++, vai ter que esperar mais um ciclo.

Vale mencionar o std::embed em mais detalhe — essa feature permite incluir arquivos binários diretamente no executável em tempo de compilação. Shader files, certificados TLS, dados de configuração, assets de jogos. Tudo que antes exigia xxd ou ferramentas similares agora tem suporte nativo:

constexpr auto shader = std::embed("vertex_shader.glsl");
constexpr auto cert = std::embed("ca-certificate.pem");

Simples, elegante, e resolve um problema que todo projeto C++ médio-grande encontra.

Quando você vai poder usar isso?

A boa notícia: GCC e Clang já suportam cerca de dois terços das features de linguagem aprovadas. O GCC 16 já tem implementação experimental de reflection. O Clang (via fork da Bloomberg) também.

CompiladorReflectionContractsstd::execution
GCC 16+ExperimentalEm progressoParcial
Clang (Bloomberg fork)ExperimentalNão aindaParcial
MSVCPlanejadoPlanejadoParcial

A expectativa realista? Features isoladas ao longo de 2026-2027, com suporte completo em 2028 nos três compiladores principais.

Mas não precisa esperar. O Compiler Explorer já tem os forks experimentais disponíveis. Dá para brincar com reflection e contracts hoje.

O que o Herb Sutter quis dizer com “segunda onda”

O C++11 mudou a linguagem de forma fundamental. Introduziu move semantics, lambdas, smart pointers, auto, range-for. Foi tão impactante que muita gente separava mentalmente “C++ antigo” de “C++ moderno”.

O C++26 tem potencial para fazer a mesma coisa. Reflection sozinha vai mudar como bibliotecas são escritas. Contracts vão mudar como APIs são documentadas e validadas. std::execution vai mudar como paralelismo é pensado. E as melhorias de memory safety vão — aos poucos — tirar munição de quem diz que C++ é inerentemente inseguro.

A diferença é que dessa vez, o ecossistema está mais maduro. Temos package managers (Conan, vcpkg), build systems melhores (CMake 4 finalmente não é terrível), e compiladores que implementam features em meses, não anos.

Se você programa em C++, os próximos dois anos vão ser os mais empolgantes da década. E se você abandonou o C++ pelo Rust ou Go — bom, talvez seja hora de dar uma segunda olhada.

Minha recomendação? Abre o Compiler Explorer, escolhe o GCC 16 ou o Clang com reflection, e escreve uma struct com ^^. Cinco minutos com reflection e você vai entender por que o Herb Sutter está tão animado.


Fonte de inspiração: C++26 is Done — Trip Report by Herb Sutter

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts