Functors e expressões lambda
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 74 de 104.
Muitos algoritmos da STL aceitam um objeto chamável que personaliza seu comportamento. Você já viu lambdas sendo usadas com std::transform. Vamos explorar tanto functors quanto expressões lambda — duas maneiras de criar objetos chamáveis em C++.
Um functor (objeto de função) é uma classe que sobrecarrega o operator(), permitindo que as instâncias sejam chamadas como funções:
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// result: {3, 6, 9, 12}
}Cada instância de functor pode manter seu próprio estado independente por meio de variáveis-membro definidas na construção — algo que uma chamada de função comum não pode fazer. No entanto, definir uma classe para operações simples é verboso. Expressões lambda oferecem uma alternativa concisa:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });A sintaxe de lambda é [capture](parameters) { body }. A cláusula de captura especifica quais variáveis externas a lambda pode acessar. Use [=] para capturar todas por valor, [&] para capturar todas por referência ou liste variáveis específicas, como [factor] ou [&factor].
As lambdas são particularmente úteis para operações pontuais com algoritmos como ordenação com critérios personalizados:
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// Ordenado por valor absoluto: {-1, -2, 5, 8}Desafio
FácilVamos criar uma calculadora de preços que demonstre tanto functors quanto expressões lambda para aplicar diferentes estratégias de desconto aos preços dos produtos.
Você organizará seu código em dois arquivos:
Discounts.h: defina aqui seus functors de desconto e funções utilitárias.Crie um functor chamado
PercentageDiscountque armazene uma porcentagem de desconto (como um inteiro). Seuoperator()deve receber um preçodoublee retornar o preço com desconto. Por exemplo, um desconto de 20% em $100 deve retornar $80.Crie outro functor chamado
FixedDiscountque armazene um valor fixo a ser subtraído (como umdouble). Seuoperator()deve receber um preço e retornar o preço menos o valor fixo (mas nunca abaixo de 0).Crie uma função chamada
printPricesque receba umconst std::vector<double>&e imprima todos os preços separados por espaços, seguidos por uma nova linha. Formate cada preço com duas casas decimais.main.cpp: leia cinco entradas (cada uma em uma linha separada):- Preço do primeiro produto (double)
- Preço do segundo produto (double)
- Preço do terceiro produto (double)
- Desconto percentual a aplicar (inteiro, por exemplo, 20 para 20%)
- Valor do desconto fixo (double)
Crie um vetor com os três preços e demonstre ambas as abordagens:
- Imprima
Original prices:seguido pelos preços - Use
std::transformcom seu functorPercentageDiscountpara criar um novo vetor de preços com desconto. ImprimaAfter percentage discount:seguido pelos resultados - Use
std::transformcom seu functorFixedDiscountnos preços originais para criar outro vetor. ImprimaAfter fixed discount:seguido pelos resultados - Use
std::transformcom uma expressão lambda que dobre cada preço original. ImprimaPremium prices (doubled):seguido pelos resultados - Use
std::sortcom uma lambda para ordenar os preços originais em ordem decrescente. ImprimaSorted (high to low):seguido pelos preços ordenados
Por exemplo, com as entradas 100.00, 50.00, 75.00, 20 e 15.00:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 Este desafio permite comparar functors (que mantêm um estado, como o valor do desconto) com lambdas (que capturam variáveis para operações rápidas e inline). Ambas as abordagens funcionam perfeitamente com algoritmos da STL, como std::transform e std::sort.
Experimente você mesmo
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Ler as entradas
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Criar um vetor com os três preços
std::vector<double> prices = {price1, price2, price3};
// TODO: Print "Original prices:" followed by the prices using printPrices
// TODO: Use std::transform with PercentageDiscount functor
// Criar um novo vetor para os resultados
// Print "After percentage discount:" followed by the results
// TODO: Use std::transform with FixedDiscount functor on original prices
// Criar um novo vetor para os resultados
// Print "After fixed discount:" followed by the results
// TODO: Use std::transform with a lambda that doubles each original price
// Criar um novo vetor para os resultados
// Print "Premium prices (doubled):" followed by the results
// TODO: Use std::sort with a lambda to sort original prices in descending order
// Print "Sorted (high to low):" followed by the sorted prices
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online