C++ Novidades do C++11 ao C++26

Desde 2011, o C++ ganha uma versão nova a cada três anos. Cada uma traz recursos que deixam o código mais curto e mais seguro: o auto, as lambdas, os ponteiros inteligentes, o std::optional, o std::print… Esta página é um mapa: para cada versão, uma tabela com o que mudou, um exemplo mínimo, a aula do guia que ensina o assunto e se o recurso já funciona no g++ 15 e no clang 21. Cada item foi compilado e executado no laboratório.

Você vai aprender
  • Como escolher a versão do C++ na hora de compilar (-std=c++23) e como o programa descobre a versão (__cplusplus).
  • O que cada versão trouxe de mais importante: C++11, C++14, C++17, C++20, C++23 e C++26.
  • Como o mesmo programa fica em C++98 e em C++23.
  • O que do C++26 já funciona no g++ 15 e o que ainda não chegou (reflexão, contratos, std::inplace_vector).
  • Os erros que aparecem quando você usa um recurso novo com a versão errada.

A história completa das versões (quem decide, por que demorou de 1998 a 2011) está em Versões do C++. Aqui o foco é prático: o que você pode usar hoje. Todos os programas foram compilados na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cpp/novidades, com o g++ 15.2 e o clang++ 21.1 do Ubuntu 26.04.


A ideia: atualizações de um celular

Pense no celular da Ana. De tempos em tempos chega uma atualização com recursos novos: um jeito mais fácil de tirar foto, um modo escuro. Os aplicativos antigos continuam funcionando; a atualização só acrescenta coisas.

O C++ é igual. Um programa escrito em 1998 ainda compila hoje, quase sempre sem mudar nada. Cada versão nova acrescenta ferramentas. Mas existe uma diferença importante: no celular, a atualização é instalada uma vez e pronto. No C++, você escolhe a versão toda vez que compila. Se você não escolher, o compilador usa uma versão padrão, que pode ser mais antiga do que você imagina.

VersãoAnoEm uma frase
C++98 / C++031998 / 2003O C++ "clássico". O C++03 só corrigiu detalhes.
C++112011A grande virada: auto, lambdas, nullptr, ponteiros inteligentes, movimento. Chamado de "C++ moderno".
C++142014Pequenos acertos no C++11: std::make_unique, lambdas genéricas.
C++172017Ferramentas do dia a dia: optional, variant, string_view, desestruturação, filesystem.
C++202020A segunda grande virada: concepts, ranges, std::format, módulos, <=>.
C++232023std::print, std::expected, import std. A versão que este guia ensina.
C++262026Reflexão e contratos. Os compiladores ainda estão implementando.

1. Escolher a versão: -std=

Quem decide a versão é a opção -std= (de standard, "padrão") do compilador. Você já viu isso em Compilar com g++: o comando padrão do guia é g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.

Dentro do programa, a versão aparece num número especial chamado __cplusplus (com dois sublinhados na frente). O compilador o preenche sozinho com o ano e o mês da versão. Este programa só mostra esse número:

#include <iostream>

int main() {
    std::cout << __cplusplus << '\n';
}

A Ana compilou o mesmo arquivo com cada versão, de uma vez só, com um laço for do terminal (Bash). Não se preocupe com o laço: o que importa é a saída.

$ for s in 98 11 14 17 20 23 26; do g++ -std=c++$s versao.cpp -o versao && echo "c++$s: $(./versao)"; done
c++98: 199711
c++11: 201103
c++14: 201402
c++17: 201703
c++20: 202002
c++23: 202302
c++26: 202400          « o C++26 ainda não tem número final no g++ 15
$ g++ versao.cpp -o versao && ./versao
201703                 « sem -std: o g++ 15 usa o C++17

Repare na última linha. Sem -std=, o g++ 15 compila em C++17. Tudo o que veio no C++20 e no C++23 (std::format, std::print, ranges, contains) fica de fora. É por isso que o guia sempre escreve -std=c++23. No editor do guia, o Rodar já usa -std=c++23.

c++2c, c++2b, gnu++23: outros nomes

Antes de uma versão ter número oficial, os compiladores usam um apelido: c++2b era o C++23 e c++2c é o C++26. O g++ 15 aceita -std=c++26 e -std=c++2c (os dois são a mesma coisa). Já gnu++23 é "C++23 mais as extensões do GNU"; o guia usa sempre o c++23 puro, para o código funcionar em qualquer compilador.


2. C++11: a grande virada C++11

Depois de 13 anos sem novidades grandes, o C++11 mudou o jeito de escrever C++. Quase todo o "C++ moderno" que este guia ensina começa aqui.

RecursoExemplo mínimoPara que serveAulag++ 15
autoauto preco = 4.5;O compilador descobre o tipo sozinho.autosim
range-forfor (int n : notas)Percorrer uma coleção sem contar posições.range-forsim
Lambdas[](int x) { return x * 2; }Uma função pequena, sem nome, escrita no meio do código.Lambdassim
nullptrint* p = nullptr;"Ponteiro que não aponta para nada", no lugar do antigo NULL.nullptrsim
Ponteiros inteligentesstd::unique_ptr, std::shared_ptrMemória que se libera sozinha: adeus ao delete.Ponteiros Inteligentessim
Movimentob = std::move(a);"Mudar" os dados de lugar em vez de copiar.Semântica de Movimentosim
constexprconstexpr int q = quadrado(5);Calcular durante a compilação.constexprsim
enum classenum class Cor { vermelho, verde };Enumerações que não se misturam com int.Enumssim
Inicialização com chavesstd::vector<int> notas{7, 9, 10};Criar uma coleção já com os valores.std::vectorsim

Este programa usa todos esses recursos de uma vez. Não se preocupe em entender cada linha agora: cada uma tem a sua aula. A ideia é ver que tudo compila e roda.

#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

enum class Cor { vermelho, verde };

constexpr int quadrado(int x) { return x * x; }

int main() {
    auto preco = 4.5;                           // auto
    std::vector<int> notas{7, 9, 10};           // lista entre chaves
    for (int n : notas) std::cout << n << ' ';  // range-for
    std::cout << '\n';
    auto dobro = [](int x) { return x * 2; };   // lambda
    int* p = nullptr;                           // nullptr
    std::unique_ptr<int> u(new int(42));        // smart pointer (C++11)
    std::shared_ptr<int> s = std::make_shared<int>(7);
    std::string a = "lista de compras";
    std::string b = std::move(a);               // move
    constexpr int q = quadrado(5);              // constexpr
    Cor c = Cor::verde;                         // enum class
    std::cout << preco << ' ' << dobro(21) << ' ' << (p == nullptr) << ' '
              << *u << ' ' << *s << ' ' << b << ' ' << q << ' '
              << (c == Cor::verde) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra cpp11.cpp -o cpp11
$ ./cpp11
7 9 10
4.5 42 1 42 7 lista de compras 25 1

O 1 depois do 42 é o true de p == nullptr: o cout mostra os booleanos como 1 e 0 (veja Booleanos). O new int(42) da linha do unique_ptr é o jeito do C++11; o C++14 trouxe uma forma melhor, logo abaixo.

O mesmo programa, antes e depois

Para sentir a diferença, veja um programa da lista de compras da Ana escrito como se escrevia em 1998: um mapa de preços, uma lista e o total.

#include <iostream>
#include <map>
#include <string>
#include <vector>

int main() {
    std::map<std::string, double> precos;
    precos["pão"] = 7.5;
    precos["leite"] = 5.25;
    precos["café"] = 18.9;

    std::vector<std::string> lista;
    lista.push_back("café");
    lista.push_back("pão");

    double total = 0;
    for (std::vector<std::string>::const_iterator it = lista.begin(); it != lista.end(); ++it) {
        std::map<std::string, double>::const_iterator p = precos.find(*it);
        if (p != precos.end()) {
            std::cout << p->first << ": " << p->second << "\n";
            total += p->second;
        }
    }
    std::cout << "Total: " << total << "\n";
}

E agora o mesmo programa em C++23:

#include <map>
#include <print>
#include <string>
#include <vector>

int main() {
    const std::map<std::string, double> precos{
        {"pão", 7.5}, {"leite", 5.25}, {"café", 18.9}};
    const std::vector<std::string> lista{"café", "pão"};

    double total = 0;
    for (const auto& item : lista) {
        if (auto p = precos.find(item); p != precos.end()) {
            const auto& [nome, preco] = *p;
            std::println("{}: {}", nome, preco);
            total += preco;
        }
    }
    std::println("Total: {}", total);
}
$ g++ -std=c++98 -Wall -Wextra compras98.cpp -o compras98 && ./compras98
café: 18.9
pão: 7.5
Total: 26.4
$ g++ -std=c++23 -Wall -Wextra compras23.cpp -o compras23 && ./compras23
café: 18.9
pão: 7.5
Total: 26.4

A saída é idêntica. O que mudou foi o código:

C++98C++ modernoVersão
Três linhas precos["pão"] = 7.5;precos{{"pão", 7.5}, …}, e o mapa pode ser constC++11
std::vector<std::string>::const_iterator it = …for (const auto& item : lista)C++11
find numa linha, if na outraif (auto p = …; p != precos.end()): a variável só existe dentro do ifC++17
p->first e p->secondauto& [nome, preco] = *p;: nomes que dizem o que sãoC++17
std::cout << … << "\n"std::println("{}: {}", nome, preco)C++23

Missão: este é o programa de 1998. Troque o laço com const_iterator por um range-for com auto: for (const auto& item : lista). Dentro dele, use item no lugar de *it. O total deve continuar 26.4.

#include <iostream>
#include <map>
#include <string>
#include <vector>

int main() {
    std::map<std::string, double> precos;
    precos["pão"] = 7.5;
    precos["leite"] = 5.25;
    precos["café"] = 18.9;

    std::vector<std::string> lista;
    lista.push_back("café");
    lista.push_back("pão");

    double total = 0;
    for (std::vector<std::string>::const_iterator it = lista.begin(); it != lista.end(); ++it) {
        std::map<std::string, double>::const_iterator p = precos.find(*it);
        if (p != precos.end()) {
            std::cout << p->first << ": " << p->second << "\n";
            total += p->second;
        }
    }
    std::cout << "Total: " << total << "\n";
}
#include <iostream>
#include <map>
#include <string>
#include <vector>

int main() {
    std::map<std::string, double> precos;
    precos["pão"] = 7.5;
    precos["leite"] = 5.25;
    precos["café"] = 18.9;

    std::vector<std::string> lista;
    lista.push_back("café");
    lista.push_back("pão");

    double total = 0;
    for (const auto& item : lista) {
        auto p = precos.find(item);
        if (p != precos.end()) {
            std::cout << p->first << ": " << p->second << "\n";
            total += p->second;
        }
    }
    std::cout << "Total: " << total << "\n";
}

3. C++14: acabamento C++14

O C++14 foi uma versão pequena, que arredondou o C++11. O item mais importante para você é o std::make_unique: com ele, o new some de vez do código do dia a dia.

RecursoExemplo mínimoPara que serveAulag++ 15
std::make_uniqueauto u = std::make_unique<int>(42);Criar um unique_ptr sem escrever new.Ponteiros Inteligentessim
Lambdas genéricas[](auto x) { … }Uma lambda que aceita qualquer tipo.Lambdassim
Retorno deduzidoauto soma(int a, int b) { return a + b; }A função descobre o próprio tipo de retorno.Retornosim
Separador de dígitos1'000'000Números grandes fáceis de ler.Inteirossim
Literal binário0b1010Escrever um número em base 2.Inteirossim
#include <iostream>
#include <memory>

auto soma(int a, int b) { return a + b; }       // retorno deduzido

int main() {
    auto u = std::make_unique<int>(42);         // make_unique
    auto mostra = [](auto x) { std::cout << x << '\n'; };  // lambda genérica
    mostra(*u);
    mostra("texto");
    mostra(3.5);
    int milhao = 1'000'000;                     // separador de dígitos
    int bin = 0b1010;                           // literal binário
    std::cout << milhao << ' ' << bin << ' ' << soma(2, 3) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra cpp14.cpp -o cpp14 && ./cpp14
42
texto                « a mesma lambda recebeu um int, um texto e um double
3.5
1000000 10 5         « 0b1010 é 10 em decimal

4. C++17: ferramentas do dia a dia C++17

O C++17 trouxe "caixinhas" que resolvem problemas comuns: um valor que pode não existir (optional), um valor que pode ser de dois tipos (variant), um texto que você só quer ler (string_view) e um jeito de mexer em pastas e arquivos (filesystem).

RecursoExemplo mínimoPara que serveAulag++ 15
Desestruturação (structured bindings)auto [fruta, qtd] = par;Dar nome às partes de um par, tupla ou struct.pair, tuple e Desestruturaçãosim
std::optionalstd::optional<int> idade;"Talvez tenha um valor, talvez não."optional e variantsim
std::variantstd::variant<int, std::string> v;Um valor que pode ser de um tipo ou de outro.optional e variantsim
std::string_viewvoid saudar(std::string_view nome)Olhar um texto sem copiá-lo.string_viewsim
if com inicializadorif (auto it = m.find(k); it != m.end())Criar uma variável que só existe dentro do if.if e elsesim
std::filesystemstd::filesystem::current_path()Pastas, arquivos, caminhos.Arquivossim
#include <filesystem>
#include <iostream>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <variant>

std::optional<int> buscar(const std::map<std::string, int>& m, const std::string& k) {
    if (auto it = m.find(k); it != m.end()) return it->second;  // if com inicializador
    return std::nullopt;
}

void saudar(std::string_view nome) { std::cout << "Oi, " << nome << '\n'; }

int main() {
    std::map<std::string, int> estoque{{"maçã", 12}, {"pera", 0}};
    for (const auto& [fruta, qtd] : estoque)              // structured bindings
        std::cout << fruta << ": " << qtd << '\n';
    std::cout << buscar(estoque, "maçã").value_or(-1) << ' '
              << buscar(estoque, "uva").value_or(-1) << '\n';
    std::variant<int, std::string> v = std::string("dez");
    std::cout << std::get<std::string>(v) << '\n';
    saudar("Ana");
    std::cout << std::filesystem::current_path() << '\n';
}
$ g++ -std=c++23 -Wall -Wextra cpp17.cpp -o cpp17 && ./cpp17
maçã: 12
pera: 0
12 -1                      « "uva" não existe: value_or devolveu o -1 de reserva
dez
Oi, Ana
"/home/ana/cpp/novidades"  « o caminho sai entre aspas quando vai direto para o cout

Missão: o laço usa par.first e par.second, que não dizem nada. Troque por uma desestruturação: for (const auto& [fruta, qtd] : estoque), e mostre maçã tem 12 e pera tem 0.

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> estoque{{"maçã", 12}, {"pera", 0}};
    for (const auto& par : estoque) {
        std::cout << par.first << ": " << par.second << '\n';
    }
}
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> estoque{{"maçã", 12}, {"pera", 0}};
    for (const auto& [fruta, qtd] : estoque) {
        std::cout << fruta << " tem " << qtd << '\n';
    }
}

5. C++20: a segunda grande virada C++20

O C++20 é tão grande quanto o C++11. Ele mudou como se escrevem templates (concepts), como se percorrem coleções (ranges), como se formata texto (std::format) e até como se organizam os arquivos (módulos).

RecursoExemplo mínimoPara que serveAulag++ 15
Conceptstemplate <std::integral T>Dizer que tipo um template aceita, com erros legíveis.Conceptssim
Rangesnotas | std::views::filter(…)Encadear filtros e transformações numa coleção.Rangessim
std::formatstd::format("{} + {}", a, b)Montar texto com marcadores {}.format e printsim
<=> ("nave espacial")auto operator<=>(const V&) const = default;Ganhar <, >, ==… de uma vez.Sobrecarga de Operadoressim
Módulosexport module loja; / import loja;Substituir os #include.Módulossim, com -fmodules
std::spanint soma(std::span<const int> v)"Uma janela" para um pedaço de vector ou array.std::vectorsim
contains em map/setm.contains("ana")Perguntar se uma chave existe.std::mapsim
Inicializadores designadosPonto p{.x = 1, .y = 2};Preencher uma struct dizendo o nome de cada campo.Structssim
#include <algorithm>
#include <compare>
#include <concepts>
#include <format>
#include <iostream>
#include <map>
#include <ranges>
#include <span>
#include <vector>

template <std::integral T>
T dobro(T x) { return x * 2; }

struct Versao {
    int maior, menor;
    auto operator<=>(const Versao&) const = default;
};

int soma(std::span<const int> v) { int t = 0; for (int x : v) t += x; return t; }

int main() {
    std::vector<int> notas{3, 8, 5, 10, 7};
    auto aprovadas = notas | std::views::filter([](int n) { return n >= 6; });
    for (int n : aprovadas) std::cout << n << ' ';
    std::cout << '\n';
    std::ranges::sort(notas);
    std::cout << std::format("menor={} maior={} soma={}\n", notas.front(), notas.back(), soma(notas));
    std::cout << dobro(21) << '\n';
    std::cout << (Versao{1, 2} < Versao{1, 10}) << '\n';
    std::map<std::string, int> m{{"ana", 1}};
    std::cout << m.contains("ana") << '\n';
}
$ g++ -std=c++23 -Wall -Wextra cpp20.cpp -o cpp20 && ./cpp20
8 10 7                       « só as notas maiores ou iguais a 6
menor=3 maior=10 soma=33
42
1                            « a versão 1.2 vem antes da 1.10: o <=> comparou campo por campo
1
Módulos ainda pedem uma opção extra

No g++ 15, módulos só funcionam com a opção -fmodules, além do -std=c++20 ou mais novo. O CMake e as IDEs ainda estão se acostumando com eles. A aula Módulos mostra o passo a passo; no dia a dia, os #include continuam sendo a forma mais comum.


6. C++23: a versão deste guia C++23

O C++23 completou o que o C++20 começou. O recurso que você mais vai ver no guia é o std::println: escrever na tela com marcadores {}, sem a fila de <<.

RecursoExemplo mínimoPara que serveAulag++ 15
std::print / std::printlnstd::println("Olá, {}!", nome);Mostrar texto formatado. O println já pula a linha.format e printsim
std::expectedstd::expected<int, std::string>"Um resultado ou o motivo do erro", sem exceções.optional e variantsim
std::ranges::to… | std::ranges::to<std::vector>()Transformar o resultado de um range num vector de verdade.Rangessim
import std;import std;A biblioteca padrão inteira numa linha, como módulo.Módulossim, com -fmodules (veja abaixo)
contains em stringfrase.contains("compras")Perguntar se um texto contém outro.Funções de Stringsim
std::views::enumeratefor (auto [i, nome] : std::views::enumerate(nomes))Percorrer com a posição junto.Rangessim
std::flat_mapstd::flat_map<std::string, int>Um mapa guardado num vetor: mais rápido para ler.std::mapsim
#include <expected>
#include <print>
#include <ranges>
#include <string>
#include <vector>

std::expected<int, std::string> dividir(int a, int b) {
    if (b == 0) return std::unexpected("divisão por zero");
    return a / b;
}

int main() {
    std::println("Olá, {}!", "Ana");
    auto r = dividir(10, 0);
    if (!r) std::println("erro: {}", r.error());
    std::println("{}", dividir(10, 2).value());
    auto pares = std::views::iota(1, 11)
               | std::views::filter([](int x) { return x % 2 == 0; })
               | std::ranges::to<std::vector>();
    std::println("{}", pares);
    std::string frase = "lista de compras";
    std::println("{}", frase.contains("compras"));
}
$ g++ -std=c++23 -Wall -Wextra cpp23.cpp -o cpp23 && ./cpp23
Olá, Ana!
erro: divisão por zero         « dividir(10, 0) devolveu o erro, não um número
5
[2, 4, 6, 8, 10]               « o println sabe mostrar um vector inteiro
true                           « e mostra bool como true/false, não 1/0

Repare nas duas últimas linhas. O std::println mostra um vector inteiro com colchetes e vírgulas, e mostra booleanos como true/false. Com o cout, você teria de escrever um laço para o vetor e usar std::boolalpha para o booleano.

import std; no g++ 15

O import std; troca todos os #include da biblioteca padrão por uma linha. No g++ 15 ele funciona, mas a biblioteca precisa ser compilada como módulo na primeira vez. Isso se faz acrescentando o arquivo bits/std.cc (que vem com o g++) ao comando:

import std;

int main() {
    std::println("Olá do import std!");
}
$ g++ -std=c++23 -fmodules -fsearch-include-path bits/std.cc importstd.cpp -o importstd
$ ./importstd
Olá do import std!
$ ls gcm.cache
std.gcm                « a biblioteca padrão já compilada como módulo
-fmodules
Liga os módulos no g++.
-fsearch-include-path bits/std.cc
Procura o arquivo bits/std.cc nas pastas de cabeçalhos do g++ e o compila junto. É ele que diz "o módulo std é feito destas partes".
gcm.cache/std.gcm
O resultado: a biblioteca padrão "pré-digerida". Nas próximas compilações na mesma pasta, o g++ reaproveita esse arquivo.

É um recurso promissor, mas ainda pouco usado: o guia continua com os #include, que funcionam em todo lugar. No editor do guia (Clang), use também os #include.

Missão: a Ana quer só os produtos que custam menos de 10 reais. Complete a condição do filter e termine o encadeamento com | std::ranges::to<std::vector>(). O println deve mostrar [7.5, 5.25].

#include <print>
#include <ranges>
#include <vector>

int main() {
    std::vector<double> precos{7.5, 18.9, 5.25, 32.0};
    std::vector<double> baratos = precos
        | std::views::filter([](double p) { return true; /* troque */ });
    std::println("{}", baratos);
}
#include <print>
#include <ranges>
#include <vector>

int main() {
    std::vector<double> precos{7.5, 18.9, 5.25, 32.0};
    std::vector<double> baratos = precos
        | std::views::filter([](double p) { return p < 10; })
        | std::ranges::to<std::vector>();
    std::println("{}", baratos);
}

Se você rodar a missão sem mexer, o compilador recusa. No g++, a linha principal do erro é esta:

$ g++ -std=c++23 -Wall -Wextra baratos.cpp -o baratos
baratos.cpp: In lambda function:
baratos.cpp:8:40: warning: unused parameter ‘p’ [-Wunused-parameter]
    8 |         | std::views::filter([](double p) { return true; /* troque */ });
      |                                 ~~~~~~~^
baratos.cpp: In function ‘int main()’:
baratos.cpp:8:9: error: conversion from ‘std::ranges::filter_view<std::ranges::ref_view<std::vector<double> >, main()::<lambda(double)> >’ to non-scalar type ‘std::vector<double>’ requested
    7 |     std::vector<double> baratos = precos
      |                                   ~~~~~~
    8 |         | std::views::filter([](double p) { return true; /* troque */ });
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

O aviso é só porque o p ainda não é usado. O erro, traduzido: "pediram uma conversão de filter_view para std::vector<double>". Um filter é só uma "vista" que escolhe elementos na hora em que você percorre; ele não é um vector. O ranges::to existe exatamente para fazer essa conversão. (O Clang do editor diz a mesma coisa com outras palavras: no viable conversion.)


7. C++26: o que já chegou e o que ainda não C++26

O C++26 é a versão mais nova. O comitê fechou o texto, mas os compiladores ainda estão implementando as partes. As duas novidades mais esperadas são a reflexão (o programa poder "olhar para si mesmo": listar os campos de uma struct, os nomes de um enum) e os contratos (escrever na própria função as condições que os argumentos devem cumprir). A Ana testou cada item com g++ -std=c++26 e clang++ -std=c++2c:

RecursoExemplo mínimoPara que serveg++ 15clang 21
Reflexãostd::meta::identifier_of(^^Cor)Ler, durante a compilação, os nomes e campos dos seus tipos.nãonão
Contratosint dividir(int a, int b) pre(b != 0)Condições de entrada (pre) e saída (post) de uma função.nãonão
std::inplace_vectorstd::inplace_vector<int, 3> v;Um vector com capacidade máxima fixa, sem alocar memória.nãonão
Variável "não me interessa" _auto [_, idade] = pessoa;Ignorar uma parte sem inventar um nome.simsim
= delete("motivo")void pagar(int) = delete("use double");Proibir uma chamada explicando por quê.simsim
Indexar pacotesargs...[0]Pegar o primeiro argumento de um template variádico.simsim
#embed#embed "lista.txt"Colocar o conteúdo de um arquivo dentro do programa.simsim, com aviso
Aritmética saturadastd::add_sat(a, b)Somar sem "dar a volta": o resultado para no máximo do tipo.simsim
std::println() vaziostd::println();Só pular uma linha.simsim

As colunas valem para os compiladores do laboratório (Ubuntu 26.04). O editor do guia usa o Clang 22, um pouco mais novo, então pode aceitar algum item a mais com -std=c++26. As três primeiras linhas são as mais faladas, e ainda não estão no g++ 15. Veja o que ele responde a cada uma:

$ g++ -std=c++26 -Wall -Wextra c26_reflect.cpp -o c26_reflect
c26_reflect.cpp:1:10: fatal error: meta: Arquivo ou diretório inexistente
    1 | #include <meta>
      |          ^~~~~~
compilation terminated.
$ g++ -std=c++26 -Wall -Wextra c26_inplace.cpp -o c26_inplace
c26_inplace.cpp:1:10: fatal error: inplace_vector: Arquivo ou diretório inexistente
    1 | #include <inplace_vector>
      |          ^~~~~~~~~~~~~~~~
compilation terminated.
$ g++ -std=c++26 -Wall -Wextra c26_contracts.cpp -o c26_contracts
c26_contracts.cpp:3:27: error: expected initializer before ‘pre’
    3 | int dividir(int a, int b) pre(b != 0) { return a / b; }
      |                           ^~~
c26_contracts.cpp: In function ‘int main()’:
c26_contracts.cpp:6:24: error: ‘dividir’ was not declared in this scope
    6 |     std::println("{}", dividir(10, 2));
      |                        ^~~~~~~

Os dois primeiros erros dizem a mesma coisa: o cabeçalho (<meta> da reflexão, <inplace_vector>) não existe na biblioteca do g++ 15. A parte "Arquivo ou diretório inexistente" sai em português porque vem do sistema, não do g++. No terceiro, o g++ não conhece a palavra pre naquele lugar e se perde: o segundo erro (‘dividir’ was not declared) é só consequência do primeiro. Enquanto os contratos não chegam, o jeito é conferir o argumento com um if ou um assert (veja Exceções).

Já os recursos menores funcionam. Este é o = delete("motivo"), que dá um erro com a explicação escrita por você:

void pagar(double valor);
void pagar(int) = delete("use double para dinheiro");   // C++26

int main() {
    pagar(10);
}
$ g++ -std=c++26 -Wall -Wextra c26_delete.cpp -o c26_delete
c26_delete.cpp: In function ‘int main()’:
c26_delete.cpp:5:10: error: use of deleted function ‘void pagar(int)’: use double para dinheiro
    5 |     pagar(10);
      |     ~~~~~^~~~
c26_delete.cpp:2:6: note: declared here
    2 | void pagar(int) = delete("use double para dinheiro");   // C++26
      |      ^~~~~
c26_delete.cpp:5:10: note: use ‘-fdiagnostics-all-candidates’ to display considered candidates

O 10 é um int, e a versão int foi apagada de propósito: o erro aparece com a frase "use double para dinheiro". E o _, que deixa você ignorar uma parte de uma desestruturação:

#include <print>
#include <utility>

int main() {
    auto [_, idade] = std::pair{"Ana", 30};   // _ = "não me interessa" (C++26)
    std::println("{}", idade);
}
$ g++ -std=c++26 -Wall -Wextra c26_placeholder.cpp -o c26_placeholder && ./c26_placeholder
30
Use C++26 só quando precisar

Como o C++26 ainda está chegando aos compiladores, código com -std=c++26 pode não compilar no computador de outra pessoa. Para projetos, o C++23 (ou até o C++20) é a escolha segura em 2026. O editor do guia também usa -std=c++23.


Quando dá errado

"has no member named ‘contains’": esqueceu o -std=
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> estoque{{"maçã", 12}};
    if (estoque.contains("maçã"))
        std::cout << "tem maçã\n";
}
$ g++ -Wall -Wextra contem.cpp -o contem
contem.cpp: In function ‘int main()’:
contem.cpp:7:17: error: ‘class std::map<std::__cxx11::basic_string<char>, int>’ has no member named ‘contains’
    7 |     if (estoque.contains("maçã"))
      |                 ^~~~~~~~

O que significa: "a classe std::map não tem nenhum membro chamado contains". Sem -std=, o g++ 15 compilou em C++17, e o contains só veio no C++20. O tipo comprido (std::__cxx11::basic_string<char>) é só o nome completo de std::string.

Como consertar: acrescente -std=c++23. Sempre que um recurso novo "não existe", desconfie primeiro da versão.

"‘println’ is not a member of ‘std’": versão antiga demais
$ g++ -std=c++20 -Wall -Wextra imprime.cpp -o imprime
imprime.cpp: In function ‘int main()’:
imprime.cpp:4:10: error: ‘println’ is not a member of ‘std’
    4 |     std::println("Olá, {}!", "Ana");
      |          ^~~~~~~
imprime.cpp:4:10: note: ‘std::println’ is only available from C++23 onwards
$ g++ -std=c++23 -Wall -Wextra imprime.cpp -o imprime && ./imprime
Olá, Ana!

O que significa: "println não é membro de std". Aqui o g++ ajuda: a nota da última linha diz que std::println só existe "do C++23 em diante". O #include <print> não deu erro, mas, em C++20, o arquivo existe vazio.

Como consertar: -std=c++23, como na segunda tentativa.

Código moderno num compilador configurado para C++98
$ g++ -std=c++98 -Wall -Wextra compras23.cpp -o x
compras23.cpp: In function ‘int main()’:
compras23.cpp:7:47: warning: extended initializer lists only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]
    7 |     const std::map<std::string, double> precos{
      |                                               ^
compras23.cpp:7:41: error: in C++98 ‘precos’ must be initialized by constructor, not by ‘{...}’

O que significa: o aviso diz que "listas de inicialização estendidas" (as chaves {…}) só existem a partir do C++11, e o erro diz que, em C++98, o mapa teria de ser preenchido de outro jeito. Essa saída continua por dezenas de linhas, uma para cada recurso moderno do arquivo; só as primeiras aparecem aqui.

Como consertar: isso acontece com projetos antigos (um Makefile com -std=c++98) ou IDEs mal configuradas. Troque a opção para -std=c++23. Veja onde ela fica em Make e CMake.

"fatal error: …: Arquivo ou diretório inexistente" com um cabeçalho novo

Você viu esse erro na seção do C++26, com <meta> e <inplace_vector>. Ele quer dizer que o cabeçalho não existe na biblioteca do seu compilador: ou o recurso é novo demais, ou o nome está errado (<vetor>, <strings>). Confira o nome; se estiver certo, o seu compilador ainda não tem o recurso.


Referência

Opção__cplusplusRecursos-chave
-std=c++98199711classes, templates, STL (vector, map, string)
-std=c++11201103auto, range-for, lambdas, nullptr, unique_ptr/shared_ptr, std::move, constexpr, enum class, {}
-std=c++14201402make_unique, lambdas genéricas, 1'000'000, 0b1010
-std=c++17 (padrão do g++ 15)201703desestruturação, optional, variant, string_view, if (init; cond), filesystem
-std=c++20202002concepts, ranges, format, <=>, módulos, span, contains
-std=c++23 (o guia)202302print/println, expected, ranges::to, import std, string::contains
-std=c++26202400 no g++ 15reflexão e contratos (ainda não no g++ 15), _, delete("motivo"), #embed

Exercícios

1. A Ana compilou com g++ programa.cpp -o programa, sem -std=. Em que versão o g++ 15 compilou?

2. Qual destes recursos não funciona no g++ 15?

3. Qual versão trouxe auto, lambdas, nullptr e os ponteiros inteligentes?

4. A mensagem ‘class std::map<…>’ has no member named ‘contains’ apareceu. Qual a causa mais provável?

5. Complete o comando para compilar em C++23 com os avisos ligados:

g++ -Wall -Wextra loja.cpp -o loja

6. Qual valor de __cplusplus o g++ 15 mostra com -std=c++20?


Resumo

  • Uma versão nova a cada três anos: C++11, 14, 17, 20, 23 e 26. O código antigo continua compilando.
  • Você escolhe a versão com -std=. Sem ela, o g++ 15 usa o C++17. O guia usa sempre -std=c++23.
  • C++11 e C++20 foram as grandes viradas; C++14 e C++23 completaram o que elas começaram; C++17 trouxe optional, variant e companhia.
  • Tudo do C++23 que o guia ensina funciona no g++ 15 e no clang 21. import std; funciona no g++ 15 com -fmodules e bits/std.cc.
  • Do C++26, os recursos menores já funcionam; reflexão, contratos e std::inplace_vector ainda não estão no g++ 15 nem no clang 21.
  • "Não é membro", "não existe" ou "fatal error: … inexistente" com um recurso novo: confira primeiro o -std=.