2011-09-19 8 views
70

yo estaba tratando de crear un vector de lambda, pero fracasaron:¿Por qué no puedo crear un vector de lambdas (del mismo tipo) en C++ 11?

auto ignore = [&]() { return 10; }; //1 
std::vector<decltype(ignore)> v;  //2 
v.push_back([&]() { return 100; }); //3 

hasta la línea # 2, que compiles fine. Pero la línea # 3 da compilation error:

error: no matching function for call to 'std::vector<main()::<lambda()>>::push_back(main()::<lambda()>)'

no quiero un vector de punteros a funciones o vector de objetos de función. Sin embargo, el vector de los objetos de función que encapsula real expresiones lambda, me funcionaría. es posible?

+21

"No quiero un vector de punteros de función o vector de objetos de función." Pero eso es lo que pediste. Una lambda ** es ** un objeto de función. –

+0

Muy relacionado: [¿Cuál es el tipo de lambda cuando se deduce con "auto" en C++ 11?] (Http://stackoverflow.com/q/7951377/514235). – iammilind

Respuesta

112

Cada lambda tiene un tipo diferente —, incluso si tienen la misma firma. Debe usar un contenedor de encapsulamiento en tiempo de ejecución como std::function si desea hacer algo como eso.

ej .:

std::vector<std::function<int()>> functors; 
functors.push_back([&] { return 100; }); 
functors.push_back([&] { return 10; }); 

I don't want a vector of function pointers or vector of function objects.

Bueno, me gustaría un buen trabajo que paga con un equipo de desarrolladores de cien personas para desarrollar todos mis sueños, pero eso no va a suceder, ya sea. No siempre puedes obtener lo que quieres.

+51

Administrar un equipo de desarrolladores de cien hombres me suena más como una pesadilla :) –

+10

Además, no olvides que lambdas sin captura ([] -style) puede degradarse en punteros de función. Para poder almacenar una serie de indicadores de función del mismo tipo. Tenga en cuenta que VC10 aún no implementa eso. –

+0

Por cierto, ¿no debería utilizarse capture-less en esos ejemplos de todos modos? ¿O es necesario? - Por cierto, lambda sin captura para el puntero a la función parece ser compatible con VC11. No lo probé sin embargo. – Klaim

36

Todas las expresiones lambda tienen un tipo diferente, , incluso si son idénticas carácter por carácter. Estás presionando una lambda de un tipo diferente (porque es otra expresión) en el vector, y eso obviamente no funcionará.

One solution es hacer un vector de std::function<int()> en su lugar.

auto ignore = [&]() { return 10; }; 
std::vector<std::function<int()>> v; 
v.push_back(ignore); 
v.push_back([&]() { return 100; }); 

Por otro lado, no es una buena idea usar [&] cuando no se está capturando cualquier cosa.

+8

No necesita '()' para lambdas que no tienen argumentos. – Puppy

12

Si su lambda no tiene estado, es decir, [](...){...}, C++ 11 le permite degradarse a un puntero de función. En teoría, un compilador compatible con C++ 11 sería capaz de compilar esta:

auto ignore = []() { return 10; }; //1 note misssing & in []! 
std::vector<int (*)()> v;  //2 
v.push_back([]() { return 100; }); //3 
+3

Para el registro 'auto ignore = * [] {return 10; }; 'haría que' ignore' an 'int (*)()'. –

+0

@Luc, ¡oh eso es asqueroso! ¿Cuándo agregaron eso? – MSN

+2

Bueno, dado que la función de conversión que permite tomar un puntero de función en primer lugar tiene el mandato de no ser 'explicita', la desreferenciación de una expresión lambda es válida y desreferencia el puntero resultante de la conversión. Luego, usando decaimientos 'auto' que hacen referencia nuevamente a un puntero. (El uso de 'auto &' o 'auto &&' hubiera mantenido la referencia.) –

17

Si bien lo que han dicho que es relevante, todavía es posible declarar y utilizar un vector de lambda, aunque no es muy útil :

auto lambda = [] { return 10; }; 
std::vector<decltype(lambda)> vector; 
vector.push_back(lambda); 

por lo tanto, puede almacenar cualquier número de lambdas allí, siempre y cuando sea una copia/movimiento de lambda!

+0

que en realidad podría ser útil si el retroceso ocurre en un bucle con diferentes parámetros. Presumiblemente para fines de evaluación perezosa. – MaHuJa

+6

No, no coloque los parámetros en el vector, solo el objeto de función ... Entonces sería un vector con todas las copias de la misma lambda – hariseldon78

2

Cada lambda es de un tipo diferente. Debe usar std::tuple en lugar de std::vector.

2

se puede utilizar una función de generación de lambda (actualizado con la solución sugerida por Nawaz):

#include <vector> 
#include <iostream> 

int main() { 
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ; 

    using my_lambda = decltype(lambda_gen(1)); 

    std::vector<my_lambda> vec; 

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i)); 

    int i = 0; 

    for (auto& lambda : vec){ 
     std::cout << lambda(i) << std::endl; 
     i++; 
    } 
} 

Pero creo que, básicamente, hizo su propia clase en este punto. De lo contrario, si las lambdas tienen caputres/args completamente diferentes, es probable que tengas que usar una tupla.

+0

Buena idea para envolverlo en una función como 'lambda_gen' que puede ser a su vez una lambda en sí misma Sin embargo, 'auto a = lambda_gen (1);' hace una llamada innecesaria, que se puede evitar si escribimos esto ['decltype (lambda_gen (1))'] (http://coliru.stacked-crooked.com/a/27c315671c196a56). – Nawaz

+0

¿Eso todavía hace una llamada extra? También otro punto menor es que la pregunta establece C++ 11 por lo que uno necesitaría agregar un tipo de retorno final a la función, creo. – antediluvian

+0

No. Cualquier cosa dentro de 'decltype' está * no evaluado *, por lo que la llamada no se realiza realmente. Es el mismo caso con 'sizeof' también. Además, este código no funcionará en C++ 11 incluso si agrega un tipo de devolución final. – Nawaz

Cuestiones relacionadas