Expresiones lambda en C++
En Visual C++, una expresión lambda, denominada lambda, es como una función anónima que mantiene el estado y puede obtener acceso a las variables disponibles en el ámbito de inclusión. Para ello define una clase y crea un objeto de ese tipo. En este artículo se define qué son las expresiones lambda, se comparan con otras técnicas de programación, se describen sus ventajas y se proporciona un ejemplo básico.
Acerca de las lambdas
Muchos lenguajes de programación admiten el concepto de función anónima, que es una función que tiene un cuerpo, pero no tiene nombre. Una expresión lambda es una técnica de programación relacionada con las funciones anónimas. Una expresión lambda define implícitamente una clase de objeto de función y crea un objeto de función de ese tipo de clase. Para obtener más información acerca de los objetos de función, vea Objetos de función.
Como ejemplo introductorio de una expresión lambda, el estándar ISO C++ muestra una que se emplea en el contexto de un parámetro pasado a la función std::sort():
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
En este artículo se explica cómo funciona esta expresión.
Importante
Las expresiones lambda no se admite en las entidades administradas siguientes de Common Language Runtime (CLR): ref class, ref struct, value class o value struct.
Objetos de función frente a expresionesLambdas
Cuando escribe código, probablemente utiliza punteros a función y objetos de función para resolver problemas y realizar cálculos, sobre todo cuando se usan Algoritmos de STL. Los punteros a función y los objetos de función tienen ventajas y desventajas; por ejemplo, los punteros a función tienen una sobrecarga sintáctica mínima pero no conservan el estado dentro de un ámbito, y los objetos de función pueden conservar el estado pero requieren la sobrecarga sintáctica de una definición de clase.
Una expresión lambda combina las ventajas de los punteros a función y los objetos de función y evita sus desventajas. Como los objetos de función, una expresión lambda es flexible y puede conservar el estado pero, a diferencia de un objeto de función, su sintaxis compacta no requiere una definición de clase. Mediante expresiones lambda, se puede escribir código menos complejo y menos propenso a errores que el código para un objeto de función equivalente.
En los ejemplos siguientes se compara el uso de una expresión lambda con el uso de un objeto de función. En el primer ejemplo se utiliza una expresión lambda para imprimir en la consola si cada elemento de un objeto vector es par o impar. En el segundo ejemplo se usa un objeto de función para realizar la misma tarea.
Ejemplo 1: utilizar una expresión lambda
En este ejemplo se utiliza una expresión lambda incrustada en la llamada a la función for_each para imprimir en la consola si cada elemento de un objeto vector es par o impar.
Código
// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a lambda.
int evenCount = 0;
for_each(v.begin(), v.end(), [&evenCount] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++evenCount;
} else {
cout << " is odd " << endl;
}
});
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Salida
Comentarios
En el ejemplo, el tercer argumento de la función for_each es una expresión lambda. La parte [&evenCount] especifica la cláusula capture de la expresión, (int n) especifica la lista de parámetros y la parte restante especifica el cuerpo de la expresión.
Ejemplo 2: utilizar un objeto de función
En ocasiones, una expresión lambda sería demasiado difícil de extender mucho más allá del ejemplo anterior. En el ejemplo siguiente se utiliza un objeto de función en lugar de una expresión lambda, junto con la función for_each, para generar los mismos resultados que en el ejemplo 1. Ambos ejemplos almacenan el recuento de números pares en un objeto vector. Para mantener el estado de la operación, la clase FunctorClass almacena la variable m_evenCount por referencia como una variable miembro. Para realizar la operación, FunctorClass implementa el operador de llamada a función, operator(). El compilador de Visual C++ genera código que es comparable en cuanto a tamaño y rendimiento al código con una expresión lambda del ejemplo 1. Si se trata de un problema básico como el de este artículo, probablemente el diseño lambda más simple sea mejor que el diseño de objeto de función. Sin embargo, si cree que la funcionalidad puede requerir una extensión importante en el futuro, utilice el diseño de objeto de función para que el mantenimiento del código sea más sencillo.
Para obtener más información acerca de operator(), vea Llamada de función (C++). Para obtener más información sobre la función for_each, vea for_each.
Código
// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class FunctorClass
{
public:
// The required constructor for this example.
explicit FunctorClass(int& evenCount)
: m_evenCount(evenCount) { }
// The function-call operator prints whether the number is
// even or odd. If the number is even, this method updates
// the counter.
void operator()(int n) const {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++m_evenCount;
} else {
cout << " is odd " << endl;
}
}
private:
// Default assignment operator to silence warning C4512.
FunctorClass& operator=(const FunctorClass&);
int& m_evenCount; // the number of even variables in the vector.
};
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a function object.
int evenCount = 0;
for_each(v.begin(), v.end(), FunctorClass(evenCount));
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Salida
Resumen
Las expresiones lambda son una técnica de programación eficaz y expresiva. Para obtener más información sobre las partes y las propiedades de una expresión lambda, vea Sintaxis de las expresiones lambda. Para obtener información sobre cómo usar expresiones lambda en los programas, vea Ejemplos de expresiones lambda.