C++ moderno: o que mudou do C++11 ao C++17

1. A revolução do C++11: fundamentos do C++ moderno

O C++11 marcou uma transformação profunda na linguagem, introduzindo conceitos que redefiniram a forma como escrevemos código C++. A inferência de tipos com auto e decltype eliminou declarações verbosas e melhorou a legibilidade:

auto x = 42;                    // int
auto y = 3.14;                  // double
decltype(x) z = 10;             // int

A inicialização uniforme com chaves simplificou a criação de objetos:

std::vector<int> v = {1, 2, 3, 4, 5};
std::map<std::string, int> m = {{"um", 1}, {"dois", 2}};

Rvalue references e move semantics revolucionaram o desempenho, eliminando cópias desnecessárias:

std::vector<int> criarVetor() {
    std::vector<int> v(1000000);
    return v;  // Move semantics: sem cópia
}

std::vector<int> v2 = std::move(v1);  // Transferência de recursos

2. Gerenciamento de memória inteligente e segurança

Smart pointers substituíram o gerenciamento manual de memória, prevenindo vazamentos e dupla liberação:

std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::shared_ptr<int> compartilhado = std::make_shared<int>(100);
std::weak_ptr<int> fraco = compartilhado;

nullptr eliminou ambiguidades com ponteiros nulos:

void funcao(int* ptr) {}
void funcao(int valor) {}

funcao(nullptr);  // Chama a versão com ponteiro

noexcept melhorou a segurança com exceções:

void operacaoSegura() noexcept {
    // Esta função não lança exceções
}

3. Programação genérica e templates aprimorados

Variadic templates permitiram funções com número variável de argumentos:

template<typename... Args>
void imprimir(Args... args) {
    (std::cout << ... << args) << std::endl;
}

imprimir(1, " texto ", 3.14);  // Saída: 1 texto 3.14

Lambdas genéricas (C++14) simplificaram algoritmos:

auto lambda = [](auto x, auto y) { return x + y; };
std::cout << lambda(1, 2) << " " << lambda(1.5, 2.5);

constexpr expandiu a computação em tempo de compilação:

constexpr int fatorial(int n) {
    return n <= 1 ? 1 : n * fatorial(n - 1);
}
constexpr int valor = fatorial(5);  // Computado em tempo de compilação

4. Concorrência e paralelismo nativos

C++11 introduziu threads e sincronização nativas:

std::mutex mtx;
std::thread t1([&mtx]() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Thread 1" << std::endl;
});
t1.join();

std::async e std::future simplificaram tarefas assíncronas:

auto futuro = std::async(std::launch::async, []() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});
std::cout << futuro.get();  // Aguarda e obtém o resultado

C++17 adicionou políticas de execução para algoritmos paralelos:

std::vector<int> dados(1000000);
std::sort(std::execution::par, dados.begin(), dados.end());

5. Melhorias na biblioteca padrão (C++11 a C++17)

Novos contêineres otimizaram operações comuns:

std::unordered_map<std::string, int> mapa;  // Hash table
std::array<int, 5> arr = {1, 2, 3, 4, 5};  // Array estático com STL
std::forward_list<int> lista;               // Lista simplesmente encadeada

C++17 introduziu tipos utilitários poderosos:

std::optional<int> valor = 42;
std::variant<int, double, std::string> var = "texto";
std::any qualquer = 3.14;

Expressões regulares foram integradas à biblioteca padrão:

std::regex padrao(R"(\d+)");
std::smatch match;
std::string texto = "abc123def456";
std::regex_search(texto, match, padrao);
std::cout << match[0];  // "123"

6. Expressões lambda e programação funcional

Lambdas evoluíram significativamente, com captura por movimento e lambdas genéricas:

auto recurso = std::make_unique<int>(100);
auto lambda = [recurso = std::move(recurso)]() {
    return *recurso;
};

std::function e std::bind foram amplamente substituídos por lambdas mais elegantes:

// Antigo com std::bind
auto antigo = std::bind(std::multiplies<int>(), 2, std::placeholders::_1);

// Moderno com lambda
auto moderno = [](int x) { return x * 2; };

C++17 adicionou if constexpr e fold expressions:

template<typename T>
auto processar(T valor) {
    if constexpr (std::is_integral_v<T>) {
        return valor * 2;
    } else {
        return valor;
    }
}

7. Estruturas de dados e pattern matching

Structured bindings (C++17) simplificaram a desestruturação:

std::tuple<int, double, std::string> tupla(1, 2.5, "teste");
auto [a, b, c] = tupla;

std::map<std::string, int> mapa = {{"um", 1}, {"dois", 2}};
for (const auto& [chave, valor] : mapa) {
    std::cout << chave << ": " << valor << std::endl;
}

if e switch com inicializadores melhoraram o escopo:

if (auto it = mapa.find("um"); it != mapa.end()) {
    std::cout << it->second;
}

std::string_view permitiu visualizações de string sem cópia:

void processar(std::string_view sv) {
    std::cout << sv.substr(0, 3);
}
std::string texto = "exemplo longo";
processar(texto);  // Sem cópia

8. Compilação condicional e atributos modernos

Atributos padronizados melhoraram a intenção do código:

[[nodiscard]] int calcular() { return 42; }
[[maybe_unused]] int nao_usado = 100;
switch(valor) {
    case 1: [[fallthrough]];
    case 2: break;
}

static_assert sem mensagem (C++17) simplificou verificações:

static_assert(sizeof(int) == 4);

Variáveis inline eliminaram duplicação de definições:

// header.h
inline int contador = 0;  // Definição única em múltiplas unidades de tradução

A evolução do C++11 ao C++17 transformou a linguagem em uma ferramenta mais segura, expressiva e eficiente. Cada padrão trouxe melhorias incrementais que, combinadas, permitem escrever código mais limpo, com menos bugs e melhor desempenho. O C++ moderno não é apenas uma atualização — é uma nova forma de pensar sobre programação em C++.

Referências