C++ Algoritmos
A biblioteca padrão do C++ traz dezenas de algoritmos prontos: funções que ordenam, procuram, contam, somam, copiam e apagam itens de uma coleção. Esta página é uma consulta rápida: cada linha tem a forma de chamar (simplificada), o que a função faz, um exemplo e o resultado real, conferido com o g++ 15 na máquina da Ana. Para aprender com calma, veja as aulas Algoritmos, Iteradores, Lambdas e Ranges.
- Quase todos os exemplos usam este vetor (cada exemplo começa do zero, com os valores originais):
std::vector<int> v{5, 3, 8, 1, 3}; - Resultado mostra um valor (
20,true) ou como o vetor ficou, no formato em que ostd::println("{}", v)do C++23 escreve um vetor:[1, 3, 3, 5, 8]. Veja std::format e std::print. - Os algoritmos clássicos recebem um começo e um fim:
v.begin()aponta o primeiro item ev.end()aponta um depois do último. Na assinatura, eles aparecem como(início, fim). - Muitos devolvem um iterador (um "marcador de posição"), e não o valor. Para ver o valor, use
*it; para saber a posição,it - v.begin(). "Posição 2" quer dizer o terceiro item (a contagem começa em 0). Se nada foi achado, o iterador é igual av.end(). Veja Iteradores. predé um predicado: uma função que recebe um item e respondetrueoufalse, quase sempre uma lambda como[](int x) { return x > 5; }.compé uma comparação: recebe dois itens e responde "o primeiro vem antes?".- Todos os exemplos foram compilados com
g++ -std=c++23 -Wall -Wextra(g++ 15.2) e também com o clang++ 21.
Atalhos: Cabeçalhos · Ordenar · Procurar · Contar e testar · Mínimo e máximo · Somar e acumular · Transformar, copiar, preencher · Remover · Busca binária · std::ranges · Views · Armadilhas
A ideia: uma caixa de ferramentas
Imagine que a Ana tem uma pilha de provas para corrigir. Ela pode inventar, toda vez, um jeito de pôr as provas em ordem, achar a maior nota ou contar quantos passaram. Ou pode abrir a caixa de ferramentas da escola, onde já existe uma ferramenta pronta para cada tarefa, testada por milhares de pessoas.
Os algoritmos são essa caixa. Em vez de escrever um laço for para achar o maior número, você chama std::max_element. O código fica menor, diz o que faz (e não só como) e tem menos chance de erro.
Um programa completo, com quatro algoritmos:
#include <algorithm> // sort, count, max_element...
#include <iostream>
#include <numeric> // accumulate
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
std::sort(v.begin(), v.end());
for (int x : v) {
std::cout << x << ' ';
}
std::cout << '\n';
std::cout << "quantos 3: " << std::count(v.begin(), v.end(), 3) << '\n';
std::cout << "soma: " << std::accumulate(v.begin(), v.end(), 0) << '\n';
std::cout << "maior: " << *std::max_element(v.begin(), v.end()) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra exemplo.cpp -o exemplo $ ./exemplo 1 3 3 5 8 quantos 3: 2 soma: 20 maior: 8
- #include <algorithm>
- Traz a maioria dos algoritmos:
sort,find,count,max_element… Inclua sempre. Às vezes o programa compila sem ele, porque outro cabeçalho o traz "de carona" (no g++ 15, um programa só com<iostream>e<vector>aceitou ostd::sort), mas isso não é garantido e pode quebrar em outro compilador. - #include <numeric>
- Traz os algoritmos "de conta":
accumulate,reduce,iota. - std::sort(v.begin(), v.end())
- Ordena do começo ao fim, do menor para o maior. Muda o próprio vetor.
- std::count(v.begin(), v.end(), 3)
- Conta quantos itens são iguais a 3. Não muda nada.
- std::accumulate(…, 0)
- Soma tudo, começando do valor inicial
0. - *std::max_element(…)
max_elementdevolve um iterador que aponta o maior; o*pega o valor que está lá.
Missão: o programa ordena do menor para o maior. Faça-o ordenar do maior para o menor, passando std::greater<>() como terceiro argumento do std::sort.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
std::sort(v.begin(), v.end());
for (int x : v) {
std::cout << x << ' ';
}
std::cout << '\n';
}
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
std::sort(v.begin(), v.end(), std::greater<>());
for (int x : v) {
std::cout << x << ' ';
}
std::cout << '\n';
}
Cabeçalhos: onde mora cada um
| Cabeçalho | O que traz |
|---|---|
<algorithm> | Quase tudo: sort, stable_sort, find, find_if, count, count_if, min_element, max_element, min, max, clamp, transform, copy, copy_if, fill, reverse, unique, remove, binary_search, lower_bound, any_of… e as versões std::ranges:: de todos eles. |
<numeric> | accumulate, reduce, iota, inner_product, gcd, lcm. |
<ranges> | As views (std::views::filter, transform, take…) e std::ranges::to. |
<functional> | As comparações prontas std::greater<>, std::less<>, std::plus<>, std::multiplies<> (o <algorithm> do g++ já as inclui, mas não conte com isso em outro compilador). |
<vector>, <string>… | std::erase e std::erase_if C++20 moram no cabeçalho do próprio container. |
Ordenar
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
sort(início, fim) | Ordena do menor para o maior (usa o <). Muda o container. Só funciona com containers de acesso direto (vector, array, deque, string); veja a armadilha do std::list. | std::sort(v.begin(), v.end()); | [1, 3, 3, 5, 8] |
sort(início, fim, comp) | Ordena com a sua regra: comp(a, b) responde "a vem antes de b?". | std::sort(v.begin(), v.end(), std::greater<>());std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); | [8, 5, 3, 3, 1][8, 5, 3, 3, 1] |
stable_sort(início, fim, comp) | Igual ao sort, mas estável: itens "empatados" ficam na ordem em que estavam. Útil para ordenar por um critério sem bagunçar outro. | Turma {Caio 8, Ana 9, Bia 8, Davi 9} ordenada por nota, maior primeiro:std::stable_sort(t.begin(), t.end(), [](const Aluno& a, const Aluno& b) { return a.nota > b.nota; }); | Ana 9, Davi 9, Caio 8, Bia 8(Ana continua antes de Davi; Caio antes de Bia) |
reverse(início, fim) | Inverte a ordem. Não ordena: só vira de trás para a frente. | std::reverse(v.begin(), v.end()); | [3, 1, 8, 3, 5] |
Texto é ordenado pelo código de cada letra: maiúsculas vêm antes das minúsculas, e letras acentuadas vão para o fim. Ordenando {"uva", "Maçã", "banana", "abacaxi"} com std::ranges::sort, o resultado é ["Maçã", "abacaxi", "banana", "uva"]. Veja Acentos e UTF-8.
Procurar
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
find(início, fim, valor) | Iterador para o primeiro item igual a valor, ou fim se não achar. Sempre compare com v.end() antes de usar *it. | auto it = std::find(v.begin(), v.end(), 8);it - v.begin()std::find(v.begin(), v.end(), 7) == v.end() | 2 (posição)true (não achou) |
find_if(início, fim, pred) | O primeiro item para o qual pred responde true. | auto it = std::find_if(v.begin(), v.end(), [](int x) { return x > 5; });*it | 8 (posição 2) |
ranges::contains(v, valor) C++23 | Só responde se está lá (true/false). Antes do C++23: std::find(…) != v.end(). | std::ranges::contains(v, 8)std::ranges::contains(v, 7) | truefalse |
s.find(…) (texto) | Para procurar dentro de uma std::string, use o método dela, que devolve uma posição numérica. Veja std::string. | — | — |
Contar e testar
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
count(início, fim, valor) | Quantos itens são iguais a valor. | std::count(v.begin(), v.end(), 3) | 2 |
count_if(início, fim, pred) | Quantos itens fazem pred responder true. | std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }) | 1 (só o 8 é par) |
any_of(início, fim, pred) | Pelo menos um item satisfaz? | std::any_of(v.begin(), v.end(), [](int x) { return x > 7; }) | true |
all_of(início, fim, pred) | Todos satisfazem? (Num vetor vazio, responde true.) | std::all_of(v.begin(), v.end(), [](int x) { return x > 0; }) | true |
none_of(início, fim, pred) | Nenhum satisfaz? | std::none_of(v.begin(), v.end(), [](int x) { return x < 0; }) | true |
equal(início, fim, início2)ranges::equal(a, b) | As duas sequências têm os mesmos itens, na mesma ordem? (Para dois vector, a == b já basta.) | std::ranges::equal(v, std::vector<int>{5, 3, 8, 1, 3}) | true |
O count funciona também em texto: std::ranges::count(std::string{"banana"}, 'a') dá 3.
Mínimo e máximo
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
min_element(início, fim) | Iterador para o menor item (o primeiro, se houver empate). Num vetor vazio, devolve fim: não use * sem conferir. | auto it = std::min_element(v.begin(), v.end());*it e it - v.begin() | 1, posição 3 |
max_element(início, fim) | Iterador para o maior item. | *std::max_element(v.begin(), v.end()) | 8 (posição 2) |
minmax_element(início, fim) | Os dois de uma vez, num par de iteradores. | auto [a, b] = std::minmax_element(v.begin(), v.end());*a e *b | 1 e 8 |
min(a, b), max(a, b) | O menor/maior de dois valores (do mesmo tipo). | std::min(3, 7)std::max(3, 7) | 37 |
max({a, b, c…}) | O maior de uma lista entre chaves. | std::max({4, 9, 2}) | 9 |
clamp(x, menor, maior) C++17 | "Prende" x no intervalo: abaixo de menor vira menor, acima de maior vira maior. | std::clamp(15, 0, 10) | 10 |
ranges::min(v), ranges::max(v) C++20 | O valor menor/maior de uma coleção inteira (não um iterador). A coleção não pode estar vazia. | std::ranges::min(v)std::ranges::max(v) | 18 |
Somar e acumular (<numeric>)
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
accumulate(início, fim, inicial) | Soma tudo a partir de inicial. O tipo do resultado é o tipo de inicial: use 0.0 para somar double (veja a armadilha). | std::accumulate(v.begin(), v.end(), 0) | 20 |
accumulate(início, fim, inicial, op) | "Acumula" com outra operação no lugar do +. | std::accumulate(v.begin(), v.end(), 1, std::multiplies<>()) | 360 (5×3×8×1×3) |
reduce(início, fim) C++17 | Soma, como o accumulate, mas pode somar em qualquer ordem (e até em paralelo). Sem valor inicial, começa do zero do tipo dos itens. | std::reduce(v.begin(), v.end()) | 20 |
iota(início, fim, primeiro) | Preenche com primeiro, primeiro+1, primeiro+2… (o nome vem da letra grega ι). | std::vector<int> i(5);std::iota(i.begin(), i.end(), 1); | [1, 2, 3, 4, 5] |
ranges::fold_left(v, inicial, op) C++23 | O accumulate da família ranges (em <algorithm>). | std::ranges::fold_left(v, 0, std::plus<>()) | 20 |
Média de notas: some com 0.0 e divida pela quantidade. Com std::vector<double> notas{9.5, 7.0, 8.5};, std::accumulate(notas.begin(), notas.end(), 0.0) / notas.size() dá 8.333333333333334.
Transformar, copiar e preencher
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
transform(início, fim, destino, f) | Aplica f em cada item e escreve o resultado em destino. O destino precisa ter espaço (ou ser o próprio vetor). | std::vector<int> d(v.size());std::transform(v.begin(), v.end(), d.begin(), [](int x) { return x * 2; });d | [10, 6, 16, 2, 6] |
| (no próprio vetor) | Destino = v.begin(): troca cada item pelo resultado. | std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 10; }); | [50, 30, 80, 10, 30] |
copy(início, fim, destino) | Copia tudo para destino (que precisa ter espaço). | std::vector<int> c(v.size());std::copy(v.begin(), v.end(), c.begin()); | c = [5, 3, 8, 1, 3] |
copy_if(início, fim, destino, pred) | Copia só os itens que satisfazem pred. Com std::back_inserter(p) como destino, cada cópia vira um push_back e o vetor cresce sozinho. | std::vector<int> p;std::copy_if(v.begin(), v.end(), std::back_inserter(p), [](int x) { return x > 2; }); | p = [5, 3, 8, 3] |
fill(início, fim, valor) | Põe valor em todas as posições. | std::vector<int> f(4);std::fill(f.begin(), f.end(), 7); | f = [7, 7, 7, 7] |
Remover itens
Aqui mora a maior surpresa da biblioteca: std::remove e std::unique não apagam nada. Eles só empurram os itens que ficam para a frente e devolvem um iterador para o "novo fim"; o tamanho do vetor continua o mesmo, e o que sobra depois do novo fim é lixo. Quem apaga de verdade é o método erase do vetor. Desde o C++20, std::erase e std::erase_if fazem tudo numa linha só.
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
erase(v, valor) C++20 | Apaga de verdade todos os itens iguais a valor; devolve quantos apagou. Use este. | auto q = std::erase(v, 3); | v = [5, 8, 1], q = 2 |
erase_if(v, pred) C++20 | Apaga todos os que satisfazem pred; devolve quantos. | auto q = std::erase_if(v, [](int x) { return x < 4; }); | v = [5, 8], q = 3 |
remove(início, fim, valor) | Sozinho, só reorganiza: devolve o novo fim e o tamanho não muda. | auto fim = std::remove(v.begin(), v.end(), 3);v e v.size() | [5, 8, 1, 1, 3] e 5 |
v.erase(remove(…), v.end()) | O jeito antigo (antes do C++20) de apagar de verdade: remove + erase, a "dupla erase-remove". | v.erase(std::remove(v.begin(), v.end(), 3), v.end()); | [5, 8, 1] |
remove_if(início, fim, pred) | Como remove, com predicado. Também precisa do erase depois (ou use std::erase_if). | v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x < 4; }), v.end()); | [5, 8] |
unique(início, fim) | Junta repetidos vizinhos num só; devolve o novo fim. Para tirar todas as repetições, ordene antes e apague depois. | std::sort(v.begin(), v.end());v.erase(std::unique(v.begin(), v.end()), v.end()); | [1, 3, 5, 8] |
Missão: tire todos os 3 do vetor com uma linha só, usando std::erase (C++20). A saída deve ser 5 8 1.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
// escreva aqui a linha que tira todos os 3
for (int x : v) {
std::cout << x << ' ';
}
std::cout << '\n';
}
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
std::erase(v, 3);
for (int x : v) {
std::cout << x << ' ';
}
std::cout << '\n';
}
Busca binária (só em coleções ordenadas)
Num vetor ordenado, dá para achar um valor muito mais rápido: olha-se o meio, descarta-se a metade em que ele não pode estar, e repete-se. É como procurar uma palavra no dicionário. Os exemplos abaixo usam o vetor já ordenado [1, 3, 3, 5, 8].
| Função | O que faz | Exemplo | Resultado |
|---|---|---|---|
binary_search(início, fim, valor) | O valor está lá? (true/false) | std::binary_search(v.begin(), v.end(), 5)std::binary_search(v.begin(), v.end(), 4) | truefalse |
lower_bound(início, fim, valor) | Iterador para o primeiro item maior ou igual a valor: onde ele entraria para manter a ordem. Se todos forem menores, devolve fim. | std::lower_bound(v.begin(), v.end(), 3)std::lower_bound(v.begin(), v.end(), 4)std::lower_bound(v.begin(), v.end(), 9) | posição 1posição 3 (o 5)v.end() |
upper_bound(início, fim, valor) | O primeiro item maior que valor. De lower_bound até upper_bound ficam todas as cópias do valor. | std::upper_bound(v.begin(), v.end(), 3) | posição 3 (os 3 estão nas posições 1 e 2) |
A família std::ranges C++20
Desde o C++20, quase todo algoritmo tem uma versão em std::ranges:: que recebe o container inteiro, sem begin()/end(). Ela também aceita uma projeção: o campo pelo qual comparar. E dá mensagens de erro mais diretas quando o container não serve. Veja a aula Ranges.
| Clássico | Com ranges | Resultado |
|---|---|---|
std::sort(v.begin(), v.end()) | std::ranges::sort(v) | [1, 3, 3, 5, 8] |
std::sort(v.begin(), v.end(), std::greater<>()) | std::ranges::sort(v, std::ranges::greater{}) | [8, 5, 3, 3, 1] |
std::find(v.begin(), v.end(), 8) | std::ranges::find(v, 8) | iterador, posição 2 |
std::count(v.begin(), v.end(), 3) | std::ranges::count(v, 3) | 2 |
*std::max_element(v.begin(), v.end()) | std::ranges::max(v) | 8 |
std::reverse(v.begin(), v.end()) | std::ranges::reverse(v) | [3, 1, 8, 3, 5] |
std::iota(u.begin(), u.end(), 0) | std::ranges::iota(u, 0) C++23 | com u de 10 itens: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
std::accumulate(v.begin(), v.end(), 0) | std::ranges::fold_left(v, 0, std::plus<>()) C++23 | 20 |
Projeção: com struct Aluno { std::string nome; int nota; }; e a turma {Caio 8, Ana 9, Bia 8, Davi 9}:
| Exemplo | O que faz | Resultado |
|---|---|---|
std::ranges::sort(turma, {}, &Aluno::nome); | Ordena pelo campo nome. O {} quer dizer "a comparação padrão" (<). | Ana 9, Bia 8, Caio 8, Davi 9 |
std::ranges::max_element(turma, {}, &Aluno::nota)->nome | Quem tem a maior nota (a primeira, em caso de empate). | Ana |
Views: filtrar e transformar sem copiar C++20
Uma view ("visão") é uma janela sobre a coleção: ela mostra os itens de outro jeito (só os pares, multiplicados por 10, de trás para a frente) sem copiar nem mudar o original. Os itens só são calculados quando alguém percorre a view (ela é "preguiçosa"). As views se encadeiam com a barra |, como canos. Ficam em <ranges>.
| View | O que mostra | Exemplo | Resultado |
|---|---|---|---|
views::filter(pred) | Só os itens que satisfazem pred. | v | std::views::filter([](int x) { return x % 2 == 1; }) | [5, 3, 1, 3] |
views::transform(f) | Cada item passado por f. | v | std::views::transform([](int x) { return x * x; }) | [25, 9, 64, 1, 9] |
views::take(n) | Os n primeiros. | v | std::views::take(2) | [5, 3] |
views::drop(n) | Pula os n primeiros. | v | std::views::drop(2) | [8, 1, 3] |
views::reverse | De trás para a frente (sem mudar v). | v | std::views::reverse | [3, 1, 8, 3, 5] |
views::iota(a, b) | Os números de a até b - 1, sem criar vetor. Sem b, não acaba nunca (use com take). | std::views::iota(1, 6)std::views::iota(1) | std::views::take(3) | [1, 2, 3, 4, 5][1, 2, 3] |
views::enumerate C++23 | Pares (posição, item), para saber o índice num range-for. | for (auto [i, x] : v | std::views::enumerate) std::print("{}:{} ", i, x); | 0:5 1:3 2:8 3:1 4:3 |
views::zip(a, b) C++23 | Anda em duas coleções ao mesmo tempo. | for (auto [nome, nota] : std::views::zip(nomes, notas))com nomes{"Ana", "Bia", "Caio"} e notas{9.5, 7.0, 8.5} | Ana=9.5 Bia=7 Caio=8.5 |
views::keys, views::values | Só o primeiro / só o segundo de cada par (ótimo com std::map). | std::views::keys(std::views::zip(nomes, notas)) | ["Ana", "Bia", "Caio"] |
| std::ranges::to<std::vector>() C++23 | Transforma a view num vetor de verdade (uma cópia). | auto pares = v | std::views::filter( [](int x) { return x % 2 == 0; }) | std::ranges::to<std::vector>(); | [8] |
Encadeando: os itens maiores que 2, multiplicados por 10, depois de ordenar. Repare que, nas views, a ordem da leitura é a ordem em que as coisas acontecem.
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> v{5, 3, 8, 1, 3};
std::ranges::sort(v);
auto grandes = v | std::views::filter([](int x) { return x > 2; })
| std::views::transform([](int x) { return x * 10; });
for (int x : grandes) {
std::cout << x << ' ';
}
std::cout << '\n';
}
$ g++ -std=c++23 -Wall -Wextra views.cpp -o views $ ./views 30 30 50 80
- Em código novo com C++20 ou mais, prefira
std::ranges::: é mais curto, não tem como trocar obeginde um vetor peloendde outro, e aceita projeção. - A forma clássica continua valendo e aparece em todo código antigo, em livros e em respostas na internet; você precisa saber ler as duas. Ela também é a única que funciona num pedaço do vetor sem views (
std::sort(v.begin(), v.begin() + 3)). - Os algoritmos
<numeric>(accumulate,reduce) não têm versãoranges::; o substituto do C++23 é ostd::ranges::fold_left. - Use views quando quiser só olhar os itens de outro jeito; use o algoritmo quando quiser mudar o container.
Armadilhas (resultados reais)
std::remove "não removeu"
Depois de std::remove(v.begin(), v.end(), 3); sozinho, o vetor ficou [5, 8, 1, 1, 3], ainda com 5 itens. Os dois últimos são sobras. Por sorte, o g++ 15 avisa quando você joga fora o "novo fim" que o remove (e o unique) devolve:
$ g++ -std=c++23 -Wall -Wextra rem.cpp -o rem
rem.cpp: In function ‘int main()’:
rem.cpp:7:16: warning: ignoring return value of ‘constexpr _FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<int*, vector<int> >; _Tp = int]’, declared with attribute ‘nodiscard’ [-Wunused-result]
7 | std::remove(v.begin(), v.end(), 3);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/15/algorithm:63,
from rem.cpp:1:
/usr/include/c++/15/bits/stl_algo.h:772:5: note: declared here
772 | remove(_ForwardIterator __first, _ForwardIterator __last,
| ^~~~~~
O que significa: "ignorando o valor de retorno de std::remove, que foi declarada com o atributo nodiscard" (não descartar). O resultado era importante e você o jogou fora. O Clang diz o mesmo: warning: ignoring return value of function declared with 'nodiscard' attribute.
Como consertar: use std::erase(v, 3); (C++20) ou a dupla v.erase(std::remove(v.begin(), v.end(), 3), v.end());.
std::unique deixou repetidos
No vetor original [5, 3, 8, 1, 3], unique + erase não muda nada: os dois 3 não estão lado a lado.
Como consertar: ordene antes (std::sort), aí os repetidos ficam vizinhos e o resultado é [1, 3, 5, 8]. Se precisar de itens únicos sempre, considere um std::set.
binary_search num vetor fora de ordem
Em [5, 3, 8, 1, 3], sem ordenar, std::binary_search(v.begin(), v.end(), 8) deu true, mas std::binary_search(v.begin(), v.end(), 5) deu false, embora o 5 esteja lá, na primeira posição.
O que significa: a busca binária supõe que o vetor está ordenado; se não estiver, a resposta é qualquer coisa. Nenhum erro, nenhum aviso.
Como consertar: ordene antes, ou use std::find/std::ranges::contains, que funcionam em qualquer ordem.
accumulate
Com std::vector<double> notas{9.5, 7.0, 8.5};, std::accumulate(notas.begin(), notas.end(), 0) deu 24, e não 25.
O que significa: o valor inicial 0 é um int, então a conta inteira é feita em int: a cada passo, a parte decimal é jogada fora (0 + 9.5 → 9; 9 + 7 → 16; 16 + 8.5 → 24). Nem o -Wall -Wextra avisa, porque a conversão acontece dentro do cabeçalho da biblioteca.
Como consertar: comece com 0.0: std::accumulate(notas.begin(), notas.end(), 0.0) dá 25. Veja Conversão de Tipos.
*max_element (ou *find) sem conferir
Num vetor vazio, std::max_element(v.begin(), v.end()) devolve v.end(); num find que não acha, também. Usar * nesse iterador lê memória que não é do vetor: é comportamento indefinido, e o programa pode mostrar lixo, travar ou parecer funcionar.
Como consertar: confira antes: if (it != v.end()) { … *it … }, ou confira v.empty(). Os sanitizers ajudam a achar esse erro.
std::sort num std::list
#include <algorithm>
#include <list>
int main() {
std::list<int> l{5, 3, 8};
std::sort(l.begin(), l.end());
}
O g++ escreve cerca de 30 linhas, quase todas de dentro da biblioteca. A linha que importa é esta (e a required from here, que aponta a sua linha):
$ g++ -std=c++23 -Wall -Wextra lista.cpp -o lista
In file included from /usr/include/c++/15/algorithm:63,
from lista.cpp:1:
/usr/include/c++/15/bits/stl_algo.h: In instantiation of ‘constexpr void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = _List_iterator<int>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]’:
/usr/include/c++/15/bits/stl_algo.h:4841:18: required from ‘constexpr void std::sort(_RAIter, _RAIter) [with _RAIter = _List_iterator<int>]’
4841 | std::__sort(__first, __last, __gnu_cxx::__ops::__iter_less_iter());
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lista.cpp:6:14: required from here
6 | std::sort(l.begin(), l.end());
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
/usr/include/c++/15/bits/stl_algo.h:1907:50: error: no match for ‘operator-’ (operand types are ‘std::_List_iterator<int>’ and ‘std::_List_iterator<int>’)
1907 | std::__lg(__last - __first) * 2,
| ~~~~~~~^~~~~~~~~
« … mais 20 linhas de "note: candidate" …
O que significa: o sort precisa pular direto para o meio da coleção (fazer fim - início), e os iteradores de uma lista encadeada não sabem fazer essa conta: eles só andam um passo de cada vez. Com std::ranges::sort(l), o erro chega mais cedo e mais claro: required for the satisfaction of ‘random_access_range<_Range>’, ou seja, "a lista não é um range de acesso aleatório".
Como consertar: listas têm o próprio método: l.sort();. Veja list e deque. Mais sobre ler erros de template em Mensagens de Erro.
Exercícios
1. Ordenar
Complete para ordenar o vetor notas do menor para o maior:
2. Apagar de verdade
Complete a linha (C++20) que apaga do vetor v todos os números negativos:
3. Somar com casas decimais
Complete o valor inicial para que a soma de um std::vector<double> não perca as casas decimais:
4. Com std::vector<int> v{5, 3, 8, 1, 3};, o que vale std::count_if(v.begin(), v.end(), [](int x) { return x > 2; })?
5. Em [1, 3, 3, 5, 8], para qual posição aponta std::lower_bound(v.begin(), v.end(), 4)?
6. Qual destas linhas não muda o vetor v?
Missão final: o boletim da turma
Missão: sem escrever nenhum laço, use std::count_if para contar as notas maiores ou iguais a 7, std::max_element para achar a maior e std::accumulate para somar todas. A saída deve ser aprovados: 3, maior: 10 e soma: 41.
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> notas{7, 4, 9, 6, 10, 5};
// 1. conte as notas maiores ou iguais a 7 com std::count_if
int aprovados = 0;
// 2. ache a maior nota com std::max_element
int maior = 0;
// 3. some todas as notas com std::accumulate
int soma = 0;
std::cout << "aprovados: " << aprovados << '\n';
std::cout << "maior: " << maior << '\n';
std::cout << "soma: " << soma << '\n';
}
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> notas{7, 4, 9, 6, 10, 5};
// 1. conte as notas maiores ou iguais a 7 com std::count_if
auto aprovados = std::count_if(notas.begin(), notas.end(),
[](int n) { return n >= 7; });
// 2. ache a maior nota com std::max_element
int maior = *std::max_element(notas.begin(), notas.end());
// 3. some todas as notas com std::accumulate
int soma = std::accumulate(notas.begin(), notas.end(), 0);
std::cout << "aprovados: " << aprovados << '\n';
std::cout << "maior: " << maior << '\n';
std::cout << "soma: " << soma << '\n';
}
Resumo
- Os algoritmos ficam em
<algorithm>(quase todos) e<numeric>(accumulate,reduce,iota); as views, em<ranges>. - Forma clássica:
algoritmo(v.begin(), v.end(), …). Forma C++20:std::ranges::algoritmo(v, …). find,min_element,max_elementelower_bounddevolvem iteradores: compare comv.end()antes de usar*.removeeuniquenão apagam: usestd::erase/std::erase_if(C++20) ou a dupla erase-remove.uniquesó junta vizinhos.binary_search,lower_boundeupper_boundsó funcionam em coleções ordenadas.- No
accumulate, o tipo do valor inicial manda:0.0para somardouble. - Views (
filter,transform,take…) mostram os itens de outro jeito sem copiar nem mudar o original.