Contratos em C++26: Introdução a Pré-condições e Pós-condições

O novo recurso Contracts do C++26, que adiciona pré-condições e pós-condições padronizadas às declarações de funções, está despertando tanto entusiasmo quanto ceticismo. Os apoiadores o veem como uma forma de transformar “código como documentação” em invariantes verificáveis por máquina que podem ajudar na depuração, análise estática, otimização e até em ferramentas de IA, mantendo-se opcional em tempo de execução. Os críticos argumentam que a semântica é confusa — especialmente em torno da validação de entrada e de quando as verificações podem ser removidas na compilação — fazendo com que Contracts pareça uma forma mais pesada e confusa de `assert` que talvez não cumpra as promessas de verificação formal.

Papel dos Contratos e da Verificação Formal

  • Muitos veem os contratos do C++26 como uma forma de codificar triplas de Hoare (pré/pós/invariantes) e gerar condições de verificação que ferramentas/solvers possam checar.
  • Exemplos de outros ecossistemas (Racket, SPARK/Ada, verificadores especializados de C/C++) são citados como evidência de que um subconjunto restrito pode ser provado estaticamente, mesmo que o C++ completo nunca o seja.
  • Os contratos podem ajudar a isolar funções “confusas”: provar tudo ao redor delas assumindo que pré/pós se mantêm, e localizar onde a prova falha.

Comportamento em Tempo de Execução vs Validação de Entrada

  • Há amplo consenso: contratos são para “isso nunca deve acontecer; se acontecer, é um bug”, não para validação normal de entrada do usuário.
  • Várias pessoas observam que a fronteira entre “validação de entrada” e “suposições” é nebulosa e provavelmente será usada de forma inadequada na prática.
  • Contratos são comparados a asserts: papel semelhante, mas padronizados, visíveis na declaração, com múltiplos modos de avaliação (ignore, observe, checking).
  • As críticas se concentram no modelo de “ghost code”: é preciso assumir que as verificações podem ser omitidas, então elas não podem ser usadas como base para comportamento de segurança crítica.

Otimização e Desempenho

  • Alguns esperam que compiladores usem contratos como assume para otimização ou para eliminar verificações redundantes; outros dizem que esse não é um objetivo primário do design.
  • Há decepção porque, nas implementações atuais, o modo ignore (sem verificações em tempo de execução) não parece ainda orientar as otimizações.
  • Desenvolvedores de sistemas embarcados e de alto desempenho gostam de poder ativar verificações em estações de trabalho, mas desativá-las nas builds de destino.

Posicionamento, Headers e Módulos

  • Debate sobre onde colocar contratos: headers os expõem como documentação, mas podem forçar includes pesados; arquivos .cpp evitam isso, mas perdem visibilidade.
  • O padrão exige que a primeira declaração carregue os contratos, empurrando o uso para headers; alguns argumentam que módulos vão mitigar explosões de tempo de compilação.
  • Padrão sugerido: manter contratos de headers simples ou delegar para funções auxiliares definidas em outro lugar.

Complexidade da Linguagem e Comparações

  • Alguns veem o C++ como já grande demais e enxergam contratos como mais complexidade por ganho modesto; outros dizem que isso apenas formaliza um padrão usado há muito tempo.
  • Comparações foram feitas com Eiffel, D, Clojure, Racket, Rust, Go: muitas outras linguagens têm contratos ou facilidades de pré/pós-condição, com experiências mistas em utilidade no mundo real.

Utilidade, Segurança e Ferramentas

  • Os entusiastas enfatizam benefícios para IDEs, analisadores estáticos, agentes de IA e como “documentação executável” precisa.
  • Os céticos argumentam que contratos são “apenas asserts mais sofisticados”, não conseguem garantir execução e podem até ser menos seguros do que assert puro em alguns padrões.
  • No geral: otimismo cauteloso quanto ao potencial de ferramentas e verificação, e ceticismo substancial quanto ao ganho prático no dia a dia e às compensações de design.