2010-09-28 15 views
32

¿Cómo usar la expresión lambda como un parámetro de plantilla? P.ej. como una clase de comparación que inicializa un std :: set.¿Cómo usar una expresión lambda como parámetro de plantilla?

La siguiente solución debería funcionar, ya que la expresión lambda simplemente crea una estructura anónima, que debería ser apropiada como un parámetro de plantilla. Sin embargo, se generan muchos errores.

Código ejemplo:

struct A {int x; int y;}; 
std::set <A, [](const A lhs, const A &rhs) ->bool { 
    return lhs.x < rhs.x; 
    } > SetOfA; 

salida de error (estoy usando g ++ 4.5.1 compilador y --std = C++ 0x bandera de compilación):

error: ‘lhs’ cannot appear in a constant-expression 
error: ‘.’ cannot appear in a constant-expression 
error: ‘rhs’ cannot appear in a constant-expression 
error: ‘.’ cannot appear in a constant-expression 
At global scope: 
error: template argument 2 is invalid 

es que el comportamiento esperado o un error en GCC?

EDITAR

Como alguien señaló, estoy usando expresiones lambda de forma incorrecta, ya que devuelven una instancia de la estructura anónima que se están refiriendo.

Sin embargo, corregir ese error no resuelve el problema. Consigo lambda-expression in unevaluated context error para el siguiente código:

struct A {int x; int y;}; 
typedef decltype ([](const A lhs, const A &rhs) ->bool { 
    return lhs.x < rhs.x; 
    }) Comp; 
std::set <A, Comp > SetOfA; 
+1

que este etiquetados como C++ 0x. Parece más apropiado y debería obtener mejores respuestas. – JoshD

+1

@JoshD ¿No debería etiquetarse 'C++' también? 0x eventualmente se convertirá en el nuevo estándar y no me gustaría que la gente en el futuro se pierda esta pregunta porque olvidaron que la etiqueta adecuada era C++ 0x no C++. (O es tan va a migrar todas las etiquetas ++ C++ 0x a c en algún momento?) – KitsuneYMG

Respuesta

33

El segundo parámetro de plantilla de std::set espera un tipo , no una expresión , por lo que es sólo lo está utilizando erróneamente.

Se podría crear el juego de la siguiente manera:

auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; }; 
auto SetOfA = std::set <A, decltype(comp)> (comp); 
+0

expresión lambda es de hecho un tipo. Es solo otra forma de declarar una estructura anónima con el operador definido(). No consumo lambda como especificador de tipo: std :: Ser Un <, ....> B –

+11

@buratina: Si * * eran entonces un tipo '[]() {} x;' debe ser una declaración válida . La expresión lambda es solo una * instancia * de esa estructura anónima. Necesitas un 'decltype' para obtener ese tipo. – kennytm

+0

OK, ahora que se borra :) pero decltype también de alguna manera no funciona –

1

No estoy seguro si esto es lo que estás pidiendo, pero la firma de un lambda que devuelve RetType y acepta INTYPE será:

std::function<RetType(InType)> 

(Asegúrese de #include <functional>)

se puede acortar de que mediante el uso de un typedef, pero no estoy seguro de que puede utilizar para evitar decltype averiguar el tipo real (ya que la mbdas aparentemente no se pueden utilizar en este contexto)

Así que su typedef debe ser:.

typedef std::function<bool(const A &lhs, const A &rhs)> Comp 

o

using Comp = std::function<bool(const A &lhs, const A &rhs)>; 
+0

+1 porque esta es la solución, pero 'std :: function' es solo un tipo de titular. Puede * convertir * una lambda en 'función' y obtener un objeto * apuntando * a la lambda, pero ese no es su tipo original. – Potatoswatter

+0

Editar: la función no apunta a la lambda, la contiene. Pero tampoco era el tipo original de todos modos. – Potatoswatter

+0

Ah, es bueno saberlo. Supongo que la sintaxis en línea-lambda produce algún tipo generado aleatoriamente, de modo que no hay dos exactamente iguales. (Así es como lo hace C#, IIRC.) –

4

Por comparación utilizados de esta manera, usted todavía mejor con un enfoque no 0x:

struct A { int x; int y; }; 

struct cmp_by_x { 
    bool operator()(A const &a, A const &b) { 
    return a.x < b.x; 
    } 
}; 

std::set<A, cmp_by_x> set_of_a; 

sin embargo, en 0x se puede hacer cmp_by_x un tipo local (es decir, definirlo dentro de una función) cuando sea más conveniente, lo que está prohibido por C++ actual.

Además, su comparación trata A (x = 1, y = 1) y A (x = 1, y = 2) como equivalentes.Si eso no es deseado, es necesario incluir los otros valores que contribuyen a la singularidad:

struct cmp_by_x { 
    bool operator()(A const &a, A const &b) { 
    return a.x < b.x || (a.x == b.x && a.y < b.y); 
    } 
}; 
0

el problema es el último parámetro de plantilla es de tipo no un objeto, por lo que es posible que desee hacer lo siguiente

std::set <A, std::fuction<bool(const A &,const A &)>> 
       SetOfA([](const A lhs, const A &rhs) ->bool { 
                  return lhs.x < rhs.x; 
                  } > SetOfA; 

para que sea más simple que puede hacer lo siguiente:

auto func = SetOfA([](const A lhs, const A &rhs) ->bool { return lhs.x < rhs.x;} 
set <A,decltype(func)> SetOfA(func); 

aplausos

Cuestiones relacionadas