c ++ 0x: forma correcta de recibir una lambda como parámetro por referencia

82

¿Cuál es la forma correcta de definir una función que recibe un int->intparámetro lambda por referencia?

void f(std::function< int(int) >& lambda);

o

void f(auto& lambda);

No estoy seguro de que la última forma sea incluso sintaxis legal.

¿Hay otras formas de definir un parámetro lambda?

lurscher
fuente
10
¿Por qué necesitarías la lambda por referencia? ¿Te refieres const&?
deft_code

Respuestas:

83

No puede tener un autoparámetro. Básicamente tienes dos opciones:

Opción # 1: Úselo std::functioncomo lo ha mostrado.

Opción # 2: Use un parámetro de plantilla:

template<typename F>
void f(F &lambda) { /* ... */}

La opción n. ° 2 puede, en algunos casos, ser más eficiente, ya que puede evitar una posible asignación de montón para el objeto de función lambda incrustado, pero solo es posible si fse puede colocar en un encabezado como una función de plantilla. También puede aumentar los tiempos de compilación y la huella de I-cache, al igual que cualquier plantilla. Tenga en cuenta que también puede no tener ningún efecto, ya que si el objeto de la función lambda es lo suficientemente pequeño, puede representarse en línea en el std::functionobjeto.

bdonlan
fuente
1
Las plantillas también pueden mejorar la huella de I-cache, al eliminar los saltos al código general no local (en este caso, ejecute su lambda directamente, sin tener que saltar std::functionprimero a través del contenedor de propósito general )
jalf
5
Esta cita, "si el objeto de la función lambda es lo suficientemente pequeño, puede estar representado en línea en el std::functionobjeto" es engañosa. Las lambdas siempre están disponibles en línea (el compilador puede optar por no hacerlo, por supuesto). std::functionLas implementaciones generalmente usan optimización de objetos pequeños para evitar asignaciones de montón. Si una lambda tiene una lista de captura lo suficientemente pequeña , se almacenará std::functionsin usar el montón. Aparte de eso, el tamaño de una lambda no tiene ningún significado real.
deft_code
2
@bdonlan: Por cierto, ¿por qué hay &dentro void f(F & lambda)?
Nawaz
2
@bdonlan: Pero la const & asume que los miembros de la lambda (la captura por valores) no se pueden cambiar. Lo que puede no ser lo que quiere el usuario.
Nicol Bolas
2
@bdonlan Ha pasado un tiempo, pero pasar como referencia no constante no permite la construcción de una lambda temporal en la llamada a la función. Una referencia de valor r sería mejor aquí.
zennehoy
45

Yo usaría templatecomo:

template<typename Functor>
void f(Functor functor)
{
   cout << functor(10) << endl;
}

int g(int x)
{
    return x * x;
}
int main() 
{
    auto lambda = [] (int x) { cout << x * 50 << endl; return x * 100; };
    f(lambda); //pass lambda
    f(g);      //pass function 
}

Salida:

500
1000
100

Demostración: http://www.ideone.com/EayVq

Nawaz
fuente
13

Sé que han pasado 7 años, pero aquí hay una forma en que nadie más mencionó:

void foo(void (*f)(int)){
    std::cout<<"foo"<<std::endl;
    f(1); // calls lambda which takes an int and returns void
}
int main(){
    foo([](int a){std::cout<<"lambda "<<a<<std::endl;});
}

Qué salidas:

foo
lambda 1

No se necesitan plantillas o std :: function

Charco
fuente
10
esto está limitado solo a lambdas que pueden decaer a punteros de función (lambdas sin capturas), y también requiere especificar la firma exacta (lo mismo que para el std::functioncaso) mientras que la versión con plantilla no tiene esta limitación.
Andriy Tylychko
1

void f(auto& lambda);

Eso está cerca. Lo que realmente compilará es:

#include <cassert>

/*constexpr optional*/ const auto f = [](auto &&lambda)
{
  lambda();
  lambda();
};

int main()
{
  int counter = 0;
  f([&]{ ++counter; });
  assert(counter == 2);
}
Mónica Unslander
fuente