C++: Adaptadores STL

Na lição 38 aprendemos sobre objetos de função.

Agora, vamos aprender sobre adaptadores de função — transformando funções existentes na forma que você precisa.

Como blocos de LEGO, usando peças pequenas para construir algo maior.


1. Visão Geral de Adaptadores

(1) 1.1 O que são Adaptadores?

Adaptadores são templates que modificam o comportamento de objetos de função.

Adaptadores comuns:



2. std::bind — Vinculação de Argumentos

(1) 2.1 Uso Básico

std::bind é usado para vincular argumentos de função, criando novos objetos de função.

Exemplo: Vinculando argumentos (Dificuldade ⭐⭐)

▶ Exemplo 2: Exemplo de Código (Dificuldade ⭐)

CPP
#include <iostream>
#include <functional>

int add(int a, int b) {
 return a + b;
}

int main() {
 // Bind the first argument of add to 10
 auto add10 = std::bind(add, 10, std::placeholders::_1);
 
 std::cout << "add10(5) = " << add10(5) << std::endl; // Output: 15
 std::cout << "add10(20) = " << add10(20) << std::endl; // Output: 30
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
add10(5) = 15
add10(20) = 30

💡 Dica:


(2) 2.2 Reordenando Argumentos

Exemplo: Trocando a ordem dos argumentos (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <functional>

int subtract(int a, int b) {
 return a - b;
}

int main() {
 // Swap argument order
 auto reverse_subtract = std::bind(subtract,
 std::placeholders::_2,
 std::placeholders::_1);
 
 std::cout << "subtract(10, 3) = " << subtract(10, 3) << std::endl; // 7
 std::cout << "reverse(10, 3) = " << reverse_subtract(10, 3) << std::endl; // -7
 
 return 0;
}


3. std::ref — Empacotamento de Referência

(1) 3.1 O Problema: Passagem por Valor

Por padrão, algoritmos STL passam objetos de função por valor, o que significa que o estado não pode ser compartilhado.

Exemplo: Resolvendo com std::ref (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

struct Counter {
 int count = 0;
 void operator()(int) { count++; }
};

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 Counter counter;
 
 // ❌ Wrong! Pass by value, a copy of counter is called
 std::for_each(v.begin(), v.end(), counter);
 std::cout << "Count: " << counter.count << std::endl; // Output: 0
 
 // ✅ Correct! Use std::ref to pass by reference
 std::for_each(v.begin(), v.end(), std::ref(counter));
 std::cout << "Count: " << counter.count << std::endl; // Output: 5
 
 return 0;
}


4. std::not_fn — Negação

(1) 4.1 Uso Básico

std::not_fn é usado para negar o valor de retorno de um objeto de função.

Exemplo: Negando um predicado (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // Find the first even number
 auto it1 = std::find_if(v.begin(), v.end(),
 (int x) { return x % 2 == 0; });
 std::cout << "First even: " << *it1 << std::endl; // 2
 
 // Find the first odd number (negation)
 auto it2 = std::find_if(v.begin(), v.end(),
 std::not_fn((int x) { return x % 2 == 0; }));
 std::cout << "First odd: " << *it2 << std::endl; // 1
 
 return 0;
}


5. std::mem_fn — Ponteiro para Função Membro

(1) 5.1 O Problema: Ponteiros para Funções Membro São Difíceis de Usar

A sintaxe de ponteiro para função membro é complexa; std::mem_fn a simplifica.

Exemplo: Chamando funções membro (Dificuldade ⭐⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>

struct Student {
 std::string name;
 void display() const {
 std::cout << "Student: " << name << std::endl;
 }
};

int main() {
 std::vectorStudent students = {{"Zhang San"}, {"Li Si"}};
 
 // Use std::mem_fn to call member function
 std::for_each(students.begin(), students.end(),
 std::mem_fn(&Student::display));
 
 return 0;
}


6. Exemplo Abrangente

▶ Exemplo 1: Processador de Notas Flexível (Dificuldade ⭐⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
 std::vector<int> scores = {85, 92, 78, 90, 88};
 int threshold = 90;
 
 // Count how many scores are not lower than threshold
 int count = std::count_if(scores.begin(), scores.end(),
 std::bind(std::greater_equalint(),
 std::placeholders::_1,
 threshold));
 
 std::cout << "Number of scores >= " << threshold << ": " << count << std::endl;
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Number of scores >= : 

❓ Perguntas Frequentes

P: std::bind ainda é relevante no C++11? R: Sim, mas Lambda é mais recomendada. Lambda é mais concisa e tem melhor desempenho.

TEXT 📖 Somente leitura
// Using std::bind
auto f1 = std::bind(add, 10, std::placeholders::_1);

// Using Lambda (recommended)
auto f2 = (int x) { return add(10, x); };

P: Quando devo usar adaptadores? R: Quando uma função existente quase atende às suas necessidades, mas os argumentos não correspondem.

P: stack e queue podem usar outros contêineres como implementação subjacente? R: Sim! Adaptadores especificam o contêiner subjacente através de parâmetros de template: stack<int, vector<int>> usa vector; queue<int, list<int>> usa list. Por padrão, stack usa deque, e queue também usa deque.


▶ Exemplo 3: Fila (Dificuldade ⭐)

CPP
#include <iostream>
#include <queue>

int main() {
    std::queue<int> q;

    q.push(10);
    q.push(20);
    q.push(30);

    while (!q.empty()) {
        std::cout << "Front: " << q.front() << std::endl;
        q.pop();
    }

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Front: 
💡 Dica: Uma fila é uma estrutura Primeiro-a-Entrar-Primeiro-a-Sair (FIFO). Use push() para enfileirar, front() para acessar o elemento da frente e pop() para desenfileirar.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie uma stackint, empilhe 1, 2, 3 em ordem, depois faça um loop e desempilhe todos os elementos. Observe a ordem de saída.

  2. Intermediário (Dificuldade ⭐⭐): Use queue para implementar uma "fila de trabalhos de impressão" — simule múltiplos trabalhos de impressão sendo processados em ordem, exibindo o comprimento restante da fila após cada trabalho ser processado.

  3. Desafio (Dificuldade ⭐⭐⭐): Use priority_queue para implementar um "escalador de tarefas" — cada tarefa tem uma prioridade (1-10), a fila processa tarefas da prioridade mais alta para a mais baixa, com empates desempatados pela ordem de inserção.



Próxima lição: Tratamento de Exceções (#40)

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%