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.

Como ler as tabelas
  • 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 o std::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 e v.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 a v.end(). Veja Iteradores.
  • pred é um predicado: uma função que recebe um item e responde true ou false, 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 o std::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_element devolve 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çalhoO 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çãoO que fazExemploResultado
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çãoO que fazExemploResultado
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++23Só responde se está lá (true/false). Antes do C++23: std::find(…) != v.end().std::ranges::contains(v, 8)
std::ranges::contains(v, 7)
true
false
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çãoO que fazExemploResultado
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çãoO que fazExemploResultado
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)
3
7
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++20O 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)
1
8

Somar e acumular (<numeric>)

FunçãoO que fazExemploResultado
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++17Soma, 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++23O 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çãoO que fazExemploResultado
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çãoO que fazExemploResultado
erase(v, valor) C++20Apaga 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++20Apaga 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çãoO que fazExemploResultado
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)
true
false
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 1
posiçã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ássicoCom rangesResultado
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++23com 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++2320

Projeção: com struct Aluno { std::string nome; int nota; }; e a turma {Caio 8, Ana 9, Bia 8, Davi 9}:

ExemploO que fazResultado
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)->nomeQuem 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>.

ViewO que mostraExemploResultado
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::reverseDe 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++23Pares (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++23Anda 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::valuesSó 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++23Transforma 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
Clássico ou ranges: qual usar?
  • Em código novo com C++20 ou mais, prefira std::ranges::: é mais curto, não tem como trocar o begin de um vetor pelo end de 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ão ranges::; o substituto do C++23 é o std::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.

A média saiu errada com 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:

std::(notas.begin(), notas.);

2. Apagar de verdade

Complete a linha (C++20) que apaga do vetor v todos os números negativos:

std::(v, [](int x) { return x < 0; });

3. Somar com casas decimais

Complete o valor inicial para que a soma de um std::vector<double> não perca as casas decimais:

std::accumulate(precos.begin(), precos.end(), )

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_element e lower_bound devolvem iteradores: compare com v.end() antes de usar *.
  • remove e unique não apagam: use std::erase/std::erase_if (C++20) ou a dupla erase-remove. unique só junta vizinhos.
  • binary_search, lower_bound e upper_bound só funcionam em coleções ordenadas.
  • No accumulate, o tipo do valor inicial manda: 0.0 para somar double.
  • Views (filter, transform, take…) mostram os itens de outro jeito sem copiar nem mudar o original.