Teníamos una función que usaba una lambda interna que no capturaba, por ejemplo:
void foo() {
auto bar = [](int a, int b){ return a + b; }
// code using bar(x,y) a bunch of times
}
Ahora, la funcionalidad implementada por la lambda se volvió necesaria en otros lugares, así que voy a sacar la lambda de foo()
en el ámbito global/de espacio de nombres. Puedo dejarlo como un lambda, convirtiéndolo en una opción de copiar y pegar, o cambiarlo a una función adecuada:
auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2
void foo() {
// code using bar(x,y) a bunch of times
}
Cambiarlo a una función adecuada es trivial, pero me hizo preguntarme si hay alguna razón no dejarlo como un lambda? ¿Hay alguna razón para no usar lambdas en todas partes en lugar de funciones globales “regulares”?
Nicolás Bolas
Hay una razón muy importante para no usar lambdas globales: porque no es normal.
La sintaxis de función regular de C++ ha existido desde los días de C. Los programadores saben desde hace décadas qué significa dicha sintaxis y cómo funcionan (aunque es cierto que todo el asunto de la descomposición de función a puntero a veces muerde incluso a los programadores experimentados). Si un programador de C ++ de cualquier nivel de habilidad más allá del “novato total” ve una definición de función, sabe lo que está obteniendo.
Una lambda global es una bestia completamente diferente. Tiene un comportamiento diferente de una función regular. Las lambdas son objetos, mientras que las funciones no lo son. Tienen un tipo, pero ese tipo es distinto del tipo de su función. Etcétera.
Así que ahora, ha subido el listón en la comunicación con otros programadores. Un programador de C++ necesita entender las lambdas si va a entender lo que hace esta función. Y sí, esto es 2019, por lo que un programador de C++ decente debería tener una idea de cómo se ve una lambda. Pero sigue siendo un listón más alto.
E incluso si lo entienden, la pregunta en la mente de ese programador será… ¿por qué el escritor de este código lo escribió de esa manera? Y si no tiene una buena respuesta para esa pregunta (por ejemplo, porque explícitamente desear para prohibir la sobrecarga y ADL, como en los puntos de personalización de rangos), entonces debe usar el mecanismo común.
Preferir las soluciones esperadas a las novedosas cuando corresponda. Utilice el método menos complicado para transmitir su punto de vista.
-
“desde los días de C” Mucho antes mi amigo
– Carreras de ligereza en órbita
16 de diciembre de 2019 a las 3:05
-
@LightnessRaces: Mi punto principal fue expresar que la sintaxis de la función de C++ ha existido desde C, por lo que mucha gente sabe lo que es por inspección.
– Nicolás Bolas
16 de diciembre de 2019 a las 14:41
-
De acuerdo con toda esta respuesta excepto por la primera oración. “No es normal” es una declaración vaga y nebulosa. ¿Quizás lo dijo en el sentido de “es poco común y confuso”?
– einpoklum
17 dic 2019 a las 17:32
-
@einpoklum: C ++ tiene muchas cosas que podrían considerarse “poco comunes y confusas” que aún son bastante “normales” dentro de su dominio (ver metaprogramación de plantilla profunda). Creo que “normal” es perfectamente válido en este caso; no es algo que esté dentro de las “normas” de la experiencia de programación de C++, tanto históricamente como hoy.
– Nicolás Bolas
17 de diciembre de 2019 a las 18:12
-
@einpoklum: “OP se pregunta si deberíamos adoptar una práctica rara“No, el OP preguntó por qué no debería adoptar la rara práctica. La respuesta es que… es una práctica rara. Cuando se trata de una actividad social como la programación, la mejor razón para hacer lo normal es que otras personas sepan de qué se trata. Porque es normal. Las normas pueden cambiar, pero solo cuando se presenta una alternativa que tiene suficientes y claras ventajas para que valga la pena trastornar la norma.
– Nicolás Bolas
17 de diciembre de 2019 a las 21:12
AndyG
Puedo pensar en algunas razones por las que querría evitar las lambdas globales como reemplazos directos para funciones regulares:
- las funciones regulares se pueden sobrecargar; las lambdas no pueden (sin embargo, existen técnicas para simular esto)
- A pesar de que son similares a funciones, incluso una lambda que no captura como esta ocupará memoria (generalmente 1 byte para no capturar).
- como se señaló en los comentarios, los compiladores modernos optimizarán este almacenamiento bajo el como si regla
“¿Por qué no debería usar lambdas para reemplazar los funtores (clases) con estado?”
- las clases simplemente tienen menos restricciones que las lambdas y, por lo tanto, deberían ser lo primero que busque
- (datos públicos/privados, sobrecarga, métodos auxiliares, etc.)
- si la lambda tiene estado, entonces es aún más difícil razonar sobre cuándo se vuelve global.
- Deberíamos preferir crear un instancia de una clase en el ámbito más estrecho posible
- ya es difícil convertir una lambda que no captura en un puntero de función, y es imposible que una lambda especifique algo en su captura.
- Las clases nos brindan una forma sencilla de crear punteros de función, y también son con lo que muchos programadores se sienten más cómodos.
- Las lambdas con cualquier captura no se pueden construir de forma predeterminada (en C++ 20. Anteriormente, no había un constructor predeterminado en ningún caso)
-
También está el puntero implícito “this” a una lambda (incluso una que no captura) que da como resultado un parámetro adicional al llamar a la lambda en lugar de llamar a la función.
– 1201 Programa Alarma
15 de diciembre de 2019 a las 15:44
-
@1201ProgramAlarm: Ese es un buen punto; puede ser mucho más difícil obtener un puntero de función para una lambda (aunque las lambdas que no capturan pueden decaer en punteros de función regulares)
– AndyG
15 de diciembre de 2019 a las 15:47
-
Por otro lado, si se pasa a algún lugar en lugar de llamarlo directamente, tener una lambda en lugar de una función promueve la inserción. Naturalmente, se podría empaquetar el puntero de función en un
std::integral_constant
para eso…– Deduplicador
16 de diciembre de 2019 a las 2:09
-
@Deduplicador: “tener una lambda en lugar de una función promueve la inserciónPara que quede claro, esto solo se aplica si “en algún lugar” es una plantilla que toma la función a la que llama como un tipo invocable arbitrario, en lugar de un puntero de función.
– Nicolás Bolas
16 de diciembre de 2019 a las 4:00
-
@AndyG Estás olvidando la regla como si: los programas que no solicitan el tamaño de la lambda (porque … ¿por qué ?!), ni crean un puntero, no necesitan (y generalmente no lo hacen) destinar espacio para ello.
– Konrad Rodolfo
16 de diciembre de 2019 a las 14:38
miguel kenzel
¿Hay alguna razón para no usar lambdas en todas partes en lugar de funciones globales “regulares”?
Un problema de cierto nivel de complejidad requiere una solución de al menos la misma complejidad. Pero si hay una solución menos compleja para el mismo problema, entonces realmente no hay justificación para usar la más compleja. ¿Por qué introducir una complejidad que no necesitas?
Entre una lambda y una función, una función es simplemente el tipo de entidad menos complejo de los dos. No tienes que justificar no usar una lambda. Tienes que justificar el uso de uno. Una expresión lambda introduce un tipo de cierre, que es un tipo de clase sin nombre con todas las funciones de miembro especiales habituales, un operador de llamada de función y, en este caso, un operador de conversión implícito a puntero de función, y crea un objeto de ese tipo. Copiar inicializar una variable global de una expresión lambda simplemente hace mucho más que simplemente definir una función. Define un tipo de clase con seis funciones declaradas implícitamente, define dos funciones de operador más y crea un objeto. El compilador tiene que hacer mucho más. Si no necesita ninguna de las características de una lambda, entonces no use una lambda…
Después de preguntar, pensé en una razón para no hacer esto: dado que estas son variables, son propensas a Fiasco de orden de inicialización estática (https://isocpp.org/wiki/faq/ctors#static-init-order), lo que podría causar errores en el futuro.
eric duminil
las lambdas son funciones anónimas.
Si está utilizando una lambda con nombre, significa que básicamente está utilizando una función anónima con nombre. Para evitar este oxímoron, también podría usar una función.
-
Esto parece ser un argumento de la terminología, es decir. nombres y significados confusos. Se puede refutar fácilmente definiendo que una lambda con nombre ya no es anónima (duh). El problema aquí no es cómo se usa la lambda, es que “función anónima” es un nombre inapropiado y ya no es preciso cuando una lambda está vinculada a un nombre.
– Konrad Rodolfo
16 de diciembre de 2019 a las 14:34
-
@KonradRudolph: No es solo terminología, es por eso que se crearon en primer lugar. Al menos históricamente, las lambdas son funciones anónimas, y es por eso que es confuso nombrarlas, especialmente cuando en su lugar se esperarían funciones.
–Eric Duminil
16 de diciembre de 2019 a las 18:18
-
No. las lambdas son rutinariamente nombrado (generalmente solo localmente), no hay nada confuso al respecto.
– Konrad Rodolfo
17 de diciembre de 2019 a las 0:08
-
En desacuerdo con funciones anónimas, es más un funtor, pero de todos modos, no veo el problema de tener una instancia (nombrada) en lugar de forzarlos a usarlos solo como rvalue-reference. (ya que su punto también debería aplicarse en el ámbito local).
– Jarod42
17 de diciembre de 2019 a las 19:08
jarod42
si hay alguna razon para no dejarlo como lambda? ¿Hay alguna razón para no usar lambdas en todas partes en lugar de funciones globales “normales”?
Solíamos usar funciones en lugar de funtor global, por lo que rompe la coherencia y el Principio del menor asombro.
Las principales diferencias son:
- las funciones se pueden sobrecargar, mientras que los funtores no.
- las funciones se pueden encontrar con ADL, no con funtores.
-
Esto parece ser un argumento de la terminología, es decir. nombres y significados confusos. Se puede refutar fácilmente definiendo que una lambda con nombre ya no es anónima (duh). El problema aquí no es cómo se usa la lambda, es que “función anónima” es un nombre inapropiado y ya no es preciso cuando una lambda está vinculada a un nombre.
– Konrad Rodolfo
16 de diciembre de 2019 a las 14:34
-
@KonradRudolph: No es solo terminología, es por eso que se crearon en primer lugar. Al menos históricamente, las lambdas son funciones anónimas, y es por eso que es confuso nombrarlas, especialmente cuando en su lugar se esperarían funciones.
–Eric Duminil
16 de diciembre de 2019 a las 18:18
-
No. las lambdas son rutinariamente nombrado (generalmente solo localmente), no hay nada confuso al respecto.
– Konrad Rodolfo
17 de diciembre de 2019 a las 0:08
-
En desacuerdo con funciones anónimas, es más un funtor, pero de todos modos, no veo el problema de tener una instancia (nombrada) en lugar de forzarlos a usarlos solo como rvalue-reference. (ya que su punto también debería aplicarse en el ámbito local).
– Jarod42
17 de diciembre de 2019 a las 19:08
Supongo que capturar variables no deseadas conduciría a muchos errores si las lambdas no se usan con cuidado
– macrolandia
15 de diciembre de 2019 a las 15:28
algunos argumentos para esto youtube.com/watch?v=Ft3zFk7VPrE
– sudo rm -rf barra oblicua
16 de diciembre de 2019 a las 17:01