¿Es posible pasar una función lambda como puntero de función? Si es así, debo estar haciendo algo incorrectamente porque recibo un error de compilación.
Considere el siguiente ejemplo
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Cuando intento compilar esto , aparece el siguiente error de compilación:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Ese es un gran mensaje de error para digerir, pero creo que lo que obtengo es que el lambda no se puede tratar como un, constexpr
por lo tanto, no puedo pasarlo como un puntero de función. También he intentado hacer x
const, pero eso no parece ayudar.
c++
c++11
lambda
function-pointers
Cory Kramer
fuente
fuente
Respuestas:
Una lambda solo se puede convertir en un puntero de función si no captura, desde el borrador de la sección estándar C ++ 11
5.1.2
[expr.prim.lambda] dice ( énfasis mío ):Tenga en cuenta que cppreference también cubre esto en su sección sobre funciones de Lambda .
Entonces las siguientes alternativas funcionarían:
y esto también:
y como señala 5gon12eder , también puede usar
std::function
, pero tenga en cuenta questd::function
es pesado , por lo que no es una compensación sin costo.fuente
void*
como el único parámetro. Normalmente se llama "puntero de usuario". También es relativamente liviano, pero tiende a requerir que tengasmalloc
algo de espacio.La respuesta de Shafik Yaghmour explica correctamente por qué la lambda no se puede pasar como un puntero de función si tiene una captura. Me gustaría mostrar dos soluciones simples para el problema.
Use en
std::function
lugar de punteros de función sin formato.Esta es una solución muy limpia. Sin embargo, tenga en cuenta que incluye una sobrecarga adicional para el borrado de tipo (probablemente una llamada a función virtual).
Use una expresión lambda que no capture nada.
Dado que su predicado es realmente solo una constante booleana, lo siguiente solucionaría rápidamente el problema actual. Consulte esta respuesta para obtener una buena explicación de por qué y cómo funciona.
fuente
glutReshapeFunc
.std::function
o una lambda, ¿por qué no lo harían? Por lo menos es una sintaxis más legible. Por lo general, uno necesita usar un puntero de función para interactuar con las bibliotecas C (en realidad, con cualquier biblioteca externa) , y asegúrese de que no puede modificarlo para aceptar una función std :: o lambda.Las expresiones lambda, incluso las capturadas, pueden manejarse como un puntero de función (puntero a función miembro).
Es complicado porque una expresión lambda no es una función simple. En realidad es un objeto con un operador ().
Cuando eres creativo, ¡puedes usar esto! Piense en una clase de "función" en el estilo de std :: function. Si guarda el objeto, también puede usar el puntero de función.
Para usar el puntero de función, puede usar lo siguiente:
Para construir una clase que pueda comenzar a funcionar como una "std :: function", primero necesita una clase / estructura que pueda almacenar el puntero de objeto y función. También necesita un operador () para ejecutarlo:
Con esto, ahora puede ejecutar lambdas capturadas y no capturadas, tal como está utilizando el original:
Este código funciona con VS2015
Actualización 04.07.17:
fuente
operator()
implementación, por lo que si lo estoy leyendo bien, no creo que funcione llamar al lambda usando un puntero de función de estilo C , ¿verdad? Eso es lo que pedía la pregunta original.La captura de lambdas no se puede convertir en punteros de función, como señaló esta respuesta .
Sin embargo, a menudo es bastante difícil proporcionar un puntero de función a una API que solo acepta uno. El método más citado para hacerlo es proporcionar una función y llamar a un objeto estático con ella.
Esto es tedioso Llevamos esta idea más allá y automatizamos el proceso de creación
wrapper
y hacemos la vida mucho más fácil.Y úsalo como
En Vivo
Esto es esencialmente declarar una función anónima en cada aparición de
fnptr
.Tenga en cuenta que las invocaciones de
fnptr
sobrescribir lascallable
llamadas del mismo tipo previamente escritas . Remediamos esto, hasta cierto punto, con elint
parámetroN
.fuente
Un atajo para usar un lambda con un puntero de función C es este:
Usar Curl como ejemplo ( información de depuración de curl )
fuente
+
truco).Si bien el enfoque de la plantilla es inteligente por varias razones, es importante recordar el ciclo de vida de la lambda y las variables capturadas. Si se va a utilizar cualquier forma de puntero lambda y la lambda no es una continuación hacia abajo, solo se debe usar una copia [=] lambda. Es decir, incluso entonces, capturar un puntero a una variable en la pila no es SEGURO si la vida útil de esos punteros capturados (desbobinado de la pila) es menor que la vida útil de la lambda.
Una solución más simple para capturar una lambda como puntero es:
p.ej,
new std::function<void()>([=]() -> void {...}
Solo recuerde hacerlo más tarde
delete pLamdba
para asegurarse de no perder la memoria lambda. El secreto para darse cuenta aquí es que las lambdas pueden capturar lambdas (pregúntese cómo funciona) y también que, parastd::function
que funcione genéricamente, la implementación de lambda debe contener suficiente información interna para proporcionar acceso al tamaño de los datos lambda (y capturados) ( por esodelete
debería funcionar [ejecutando destructores de tipos capturados]).fuente
new
- std :: ya almacena el lambda en el montón Y evita tener que recordar llamar a delete.No es una respuesta directa, sino una ligera variación para usar el patrón de plantilla "functor" para ocultar los detalles del tipo lambda y mantener el código agradable y simple.
No estaba seguro de cómo quería usar la clase decide, así que tuve que extender la clase con una función que la usa. Vea el ejemplo completo aquí: https://godbolt.org/z/jtByqE
La forma básica de su clase podría verse así:
Donde pasa el tipo de la función como parte del tipo de clase utilizado como:
Una vez más, no estaba seguro de por qué está capturando
x
que tenía más sentido (para mí) tener un parámetro que se pasa a la lambda) para que pueda usar como:Vea el enlace para un ejemplo completo
fuente
Como lo mencionaron los demás, puede sustituir la función Lambda en lugar del puntero de función. Estoy usando este método en mi interfaz C ++ para F77 ODE solver RKSUITE.
fuente