Capítulo 07: Algoritmos da STL, Ranges e Expressões Lambda
Capítulo 07: Algoritmos da STL, Ranges e Expressões Lambda
Especialização em Sistemas com C++ • C++20 & C++23 • RAII, Smart Pointers, Templates, Concorrência e Alta Performance
🗺️ Mapa Conceitual de Algoritmos STL e Expressões Lambda Modernas
flowchart TD
STL["Algoritmos da STL C++20"] --> Ranges["Namespace std::ranges"]
STL --> Lambdas["Expressões Lambda"]
subgraph RANGES_ADV["Vantagens de std::ranges"]
NoIter["Fim do Pareamento Manual (.begin(), .end())"]
Projections["Projeções Diretas (&Objeto::campo)"]
ConceptsConstrained["Restrições Formais via Concepts"]
end
subgraph LAMBDA_ADV["Recursos Avançados de Lambdas"]
Generic["Lambdas Genéricas ([](auto x))"]
Captures["Cláusulas de Captura Explícitas ([this], [&], [=])"]
ConstexprLambda["constexpr Lambdas (Compile-Time)"]
end
Ranges --> RANGES_ADV
Lambdas --> LAMBDA_ADV
RANGES_ADV --> Exec["Execução Otimizada de Alta Performance"]
LAMBDA_ADV --> Exec
style STL fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style RANGES_ADV fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style LAMBDA_ADV fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style Exec fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🏛️ 1. O Fim da Era vec.begin(), vec.end()
Desde o C++98, a biblioteca de algoritmos (<algorithm>) exigia que toda invocação recebesse pares explícitos de iteradores. Essa convenção gerava verbosidade desnecessária e abria margem para erros graves em tempo de execução quando programadores passavam iteradores de coleções distintas acidentalmente (std::sort(v1.begin(), v2.end())).
Com o C++20 Ranges, os algoritmos foram reescritos sob o namespace std::ranges. Eles operam diretamente sobre o contêiner inteiro como uma unidade lógica:
#include <vector>
#include <algorithm>
std::vector<int> dados = {9, 2, 7, 1, 5};
// C++98 / C++11 (Verboso)
std::sort(dados.begin(), dados.end());
// C++20 (Moderno, Seguro e Conciso)
std::ranges::sort(dados);
⚙️ 2. Projeções: Ordenação Declarativa Sem Boilerplate
Uma das adições mais poderosas do std::ranges são as projeções (projections). Uma projeção é um invocável (ou ponteiro para função-membro) que transforma temporariamente a visão de um elemento durante a execução do algoritmo, sem alterar o dado armazenado.
Considere ordenar uma lista de estruturas complexas pelo campo de prioridade:
#include <vector>
#include <string>
#include <algorithm>
struct Tarefa {
int id;
std::string nome;
int prioridade;
};
std::vector<Tarefa> tarefas = {
{1, "Backup", 10},
{2, "Compilacao", 90},
{3, "Limpeza", 5}
};
// Ordena as tarefas decrescentemente pela propriedade 'prioridade' diretamente!
std::ranges::sort(tarefas, std::greater<>{}, &Tarefa::prioridade);
📦 3. Expressões Lambda Modernas e Cláusulas de Captura
As expressões lambda permitem a criação de objetos-função anônimos (functors) em linha com zero overhead de chamada indireta (já que o compilador realiza inlining agressivo).
3.1 Anatomia da Expressão Lambda:
[captura](parâmetros) especificadores -> tipo_retorno { corpo }
3.2 Regras Críticas de Captura para Sistemas Concorrentes:
[]: Captura vazia. A função é pura e pode ser convertida implicitamente em um ponteiro de função do C (void(*)()).[&]: Captura tudo por referência. Rápido, mas extremamente arriscado em operações assíncronas (risco de dangling reference caso a variável local saia de escopo antes do fim da thread).[=]: Captura tudo por valor (cópia). Seguro para transferências de escopo entre threads.[x = std::move(ptr)]: Captura por movimento (init-capture), permitindo transferir objetos não copiáveis (comostd::unique_ptr) para dentro da lambda.
📊 4. Comparativo de Algoritmos: STL Clássica vs C++20 Ranges
| Operação | Sintaxe Clássica (std::) |
Sintaxe C++20 (std::ranges::) |
|---|---|---|
| Ordenação | std::sort(v.begin(), v.end()) |
std::ranges::sort(v) |
| Busca Linear | std::find(v.begin(), v.end(), val) |
std::ranges::find(v, val) |
| Contagem Condicional | std::count_if(v.begin(), v.end(), fn) |
std::ranges::count_if(v, fn) |
| Ordenação por Membro | Lambda comparadora manual de 3 linhas | Projeção direta &Struct::membro |
| Validação de Tipos | Erros de template ilegíveis | Rejeição amigável via Concepts |
🔬 5. Laboratório de Arquitetura: Algoritmos Paralelos e Políticas de Execução (<execution>)
A biblioteca de algoritmos da STL oferece suporte nativo a paralelismo multi-core e vetorização SIMD de hardware através do cabeçalho <execution>.
5.1 As Quatro Políticas de Execução Padronizadas:
std::execution::seq: Execução estritamente sequencial na thread chamadora.std::execution::par: Execução paralela dividida entre múltiplas threads do pool do runtime.std::execution::par_unseq: Paralela e vetorizada (SIMD). Operações podem ser intercaladas em registradores vetoriais AVX-512. Não deve conter travas de mutex ou alocações!std::execution::unseq(C++20): Vetorizada em uma única thread (SIMD pipelined).
#include <algorithm>
#include <execution>
#include <vector>
std::vector<double> dataset(10'000'000, 3.14);
// Ordenação paralela multi-core que satura todos os núcleos da CPU:
std::sort(std::execution::par, dataset.begin(), dataset.end());