Funtores y expresiones lambda
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 74 de 104.
Muchos algoritmos de STL aceptan un objeto invocable que personaliza su comportamiento. Ya has visto lambdas utilizadas con std::transform. Exploremos tanto los functors como las expresiones lambda: dos formas de crear objetos invocables en C++.
Un functor (objeto función) es una clase que sobrecarga el operator(), lo que permite llamar a las instancias como funciones:
#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 instancia de functor puede llevar su propio estado independiente mediante variables miembro establecidas durante la construcción, algo que una llamada a una function ordinaria no puede hacer. Sin embargo, definir una class para operaciones simples resulta demasiado verboso. Las expresiones lambda proporcionan una alternativa concisa:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });La sintaxis de lambda es [capture](parameters) { body }. La cláusula de captura especifica a qué variables externas puede acceder la lambda. Usa [=] para captarlas todas por valor, [&] para captarlas todas por referencia, o enumera variables específicas como [factor] o [&factor].
Las lambdas son especialmente útiles para operaciones puntuales con algoritmos como la ordenación con criterios 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}Desafío
FácilConstruyamos una calculadora de precios que demuestre tanto los functors como las expresiones lambda para aplicar distintas estrategias de descuento a los precios de productos.
Organizarás tu código en dos archivos:
Discounts.h: Define aquí tus functors de descuento y las funciones auxiliares.Create un functor llamado
PercentageDiscountque almacene un porcentaje de descuento (como un integer). Suoperator()debe recibir un preciodoubley devolver el precio con descuento. Por ejemplo, un descuento del 20% sobre $100 debe devolver $80.Create otro functor llamado
FixedDiscountque almacene una cantidad fija para restar (como undouble). Suoperator()debe recibir un precio y devolver el precio menos la cantidad fija (pero never por debajo de 0).Create una function llamada
printPricesque reciba unconst std::vector<double>&e imprima all los precios separados por espacios, seguidos de un salto de línea. Da formato a cada precio con dos decimales.main.cpp: Lee cinco inputs (cada uno en una línea separada):- Precio del primer producto (double)
- Precio del segundo producto (double)
- Precio del tercer producto (double)
- Descuento porcentual que se aplicará (integer, por ejemplo, 20 para 20%)
- Cantidad del descuento fijo (double)
Create un vector con los tres precios y demuestra ambos enfoques:
- Imprime
Original prices:seguido de los precios - Usa
std::transformcon tu functorPercentageDiscountpara crear un vector nuevo de precios con descuento. ImprimeAfter percentage discount:seguido de los resultados - Usa
std::transformcon tu functorFixedDiscountsobre los precios original para crear otro vector. ImprimeAfter fixed discount:seguido de los resultados - Usa
std::transformcon una expresión lambda que doubles cada precio original. ImprimePremium prices (doubled):seguido de los resultados - Usa
std::sortcon una lambda para ordenar los precios original en order descending. ImprimeSorted (high to low):seguido de los precios ordenados
Por ejemplo, con los inputs 100.00, 50.00, 75.00, 20 y 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 desafío te permite comparar functors (que mantienen el estado, como la cantidad del descuento) con lambdas (que capturan variables para realizar operaciones rápidas y en línea). Ambos enfoques funcionan perfectamente con algoritmos STL como std::transform y std::sort.
Pruébalo tú mismo
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Leer entradas
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Crear un vector con los tres precios
std::vector<double> prices = {price1, price2, price3};
// TODO: Print "Original prices:" followed by the prices using printPrices
// TODO: Use std::transform with PercentageDiscount functor
// Crear un nuevo vector para los resultados
// Print "After percentage discount:" followed by the results
// TODO: Use std::transform with FixedDiscount functor on original prices
// Crear un nuevo vector para los resultados
// Print "After fixed discount:" followed by the results
// TODO: Use std::transform with a lambda that doubles each original price
// Crear un nuevo vector para los 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 lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados10Visión general de la STL
Visión general y filosofía de la STLContenedores de la STLIteradoresAlgoritmos de la STLFuntores y expresiones lambdaResumen - Frecuencia de palabras2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genéricoPractica por tu cuenta: Compilador de C++ online