2009-12-18 17 views
43

Tengo una pregunta sobre qué estilo se prefiere: std :: bind Vs lambda en C++ 0x. Sé que sirven, de alguna manera, diferentes propósitos, pero tomemos un ejemplo de funcionalidad que se cruza.Vincular a Lambda?

Usando lambda:

uniform_int<> distribution(1, 6); 
mt19937 engine; 
// lambda style 
auto dice = [&]() { return distribution(engine); }; 

Usando bind:

uniform_int<> distribution(1, 6); 
mt19937 engine; 
// bind style 
auto dice = bind(distribution, engine); 

Cuál debería preferimos? ¿por qué? asumiendo situaciones más complejas en comparación con el ejemplo mencionado. es decir, ¿cuáles son las ventajas/desventajas de una sobre la otra?

+1

¿hay alguna diferencia de rendimiento? velocidad, consumo de memoria, uso del montón? –

+0

@Caspin Realmente no sé si hay una diferencia en el rendimiento/consumo de memoria entre estas dos herramientas :) – AraK

+4

por cierto: las dos versiones no son equivalentes porque obligan a copiar los argumentos. Alternativa: enlace (ref (distribución), ref (motor)) – sellibitze

Respuesta

22

Como dijiste, bind y lambdas no apuntan exactamente al mismo objetivo.

Por ejemplo, para usar y componer algoritmos STL, las lambdas son claras ganadoras, en mi humilde opinión.

Para ilustrar, recuerdo una respuesta muy divertida, aquí en desbordamiento de pila, donde alguien pidió ideas de números mágicos hexagonales (como 0xDEADBEEF, 0xCAFEBABE, 0xDEADDEAD etc.) y se le dijo que si fuera un verdadero programador de C++ él simplemente tendría descargar una lista de palabras en inglés y el uso de un sencillo de una sola línea de C++ :)

#include <iterator> 
#include <string> 
#include <algorithm> 
#include <iostream> 
#include <fstream> 
#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/bind.hpp> 

int main() 
{ 
    using namespace boost::lambda; 
    std::ifstream ifs("wordsEn.txt"); 
    std::remove_copy_if(
     std::istream_iterator<std::string>(ifs), 
     std::istream_iterator<std::string>(), 
     std::ostream_iterator<std::string>(std::cout, "\n"), 
     bind(&std::string::size, _1) != 8u 
      || 
     bind(
      static_cast<std::string::size_type (std::string::*)(const char*, std::string::size_type) const>(
       &std::string::find_first_not_of 
      ), 
      _1, 
      "abcdef", 
      0u 
     ) != std::string::npos 
    ); 
} 

Este fragmento, en C++ puro 98, abra el archivo de palabras en inglés, escanear cada palabra e imprimir solamente los de longitud 8 con letras 'a', 'b', 'c', 'd', 'e' o 'f'.

Ahora, encienda C++ 0X y lambda:

#include <iterator> 
#include <string> 
#include <algorithm> 
#include <iostream> 
#include <fstream> 

int main() 
{ 
std::ifstream ifs("wordsEn.txt"); 
std::copy_if(
    std::istream_iterator<std::string>(ifs), 
    std::istream_iterator<std::string>(), 
    std::ostream_iterator<std::string>(std::cout, "\n"), 
    [](const std::string& s) 
    { 
     return (s.size() == 8 && 
       s.find_first_not_of("abcdef") == std::string::npos); 
    } 
); 
} 

Esto sigue siendo un poco pesado para leer (sobre todo por el negocio istream_iterator), pero mucho más simple que la versión unen :)

+0

Aunque las dos piezas de código no hacen lo mismo, obtuve su punto de vista muy claramente :) – AraK

+4

la lambda debería ser: [] (const std :: string & s) -> bool –

+9

@Beh Tou Cheh I piense que el tipo debe deducirse si lambda consiste en 'return ;' solamente (como lo hizo Thomas). – AraK

17

La sintaxis lamdba de C++ 0x es más legible que la sintaxis de enlace. Una vez que ingresas a más de 2 o 3 niveles, tu código se vuelve prácticamente ilegible y difícil de mantener. Preferiría la sintaxis lambda más intuitiva.

+0

No estoy de acuerdo. '[this]() {Type * this_too = this; run ([this_too]() {this_too-> f();});} 'no es legible ni intuitivo. –

+5

Es cierto que creo que las nuevas líneas podrían ayudar a su contraejemplo. Las nuevas líneas no ayudarían mucho con bind. –

3

Creo que es más una cuestión de gusto. Las personas que comprenden rápidamente nuevas tecnologías o que están familiarizadas con la programación funcional probablemente prefieran la sintaxis lambda, mientras que los programadores más conservadores preferirán definitivamente la vinculación, ya que está más a la par con la sintaxis tradicional de C++.

Dicha decisión debe tomarse en coordinación con las personas que trabajarán con el código, probablemente por mayoría de votos.

Lo que no cambia el hecho sin embargo, esa sintaxis lambda es mucho más poderosa y más limpia.

+3

Las personas en un equipo siguen cambiando. La legibilidad del código es muy importante especialmente para futuros programadores de mantenimiento. Por lo tanto, debemos ir con la solución que ofrezca más legibilidad y entre lamdba y bind, lamda definitivamente se lleva la palma. – posharma

8

Uno de los beneficios de las lambdas es que son mucho más útiles cuando se necesita agregar un poco de lógica sobre una función existente.

Con bind, se ve obligado a crear una nueva función/método/functor incluso si la lógica solo se necesita en este lugar. Necesitas encontrar un nombre apropiado y puede hacer que el código sea menos comprensible ya que potencialmente te hace dividir la lógica relacionada.

Con lambda, puede agregar la nueva lógica dentro de la lambda (pero no está obligado a hacerlo si tiene sentido crear una nueva llamada).

+1

+1. Tuve que cerrar un vector de FILE * en un dtor. En lugar de poder usar un lambda '[] (FILE * f) {if (f) fclose (f); } 'Tuve que crear una función con nombre y usar eso. La función apareció en la parte 'privada' de la clase y, por lo tanto, estaba separada por muchas líneas de la llamada 'for_each' – KitsuneYMG

0

C++ 0x lambdas esencialmente reemplazar bind. No hay nada que puedas vincular que no puedas recrear un envoltorio trivial lambda para lograr lo mismo. std :: tr1 :: bind irá por el camino de std :: bind1st, etc una vez que el soporte de lambda esté muy extendido. Lo cual es bueno, porque por alguna razón la mayoría de los programadores tienen dificultades para resolver su problema.

+3

Si bien esta respuesta no era correcta en el momento en que se publicó, es precisa para C++ 14. El enlace en el comentario anterior confirma esto ahora. –

+0

@gnzlbg Por favor, vea el comentario anterior. –

39

C++ 0x Las lambdas son monomórficas, mientras que las vinculadas pueden ser polimórficas. No puede tener algo como

auto f = [](auto a, auto b) { cout << a << ' ' << b; } 
f("test", 1.2f); 

ayb deben tener tipos conocidos. Por otro lado, se unen TR1/refuerzo/Phoenix/lambda le permite hacer esto:

struct foo 
{ 
    typedef void result_type; 

    template < typename A, typename B > 
    void operator()(A a, B b) 
    { 
    cout << a << ' ' << b; 
    } 
}; 

auto f = bind(foo(), _1, _2); 
f("test", 1.2f); // will print "test 1.2" 

Tenga en cuenta que los tipos A y B son no fijado aquí. Solo cuando se usa f, estos dos se deducirán.

+0

¿Por qué no simplemente declarar el lambda con parámetros explícitamente tipados? Eso sería una gran mejora sobre la solución de enlace que se muestra arriba. Además, si tiene una funcionalidad más compleja que desea reutilizar, lambda es aún mejor que bind, ya que no requiere una estructura, incluso si desea vincular el estado en el functor: 'plantilla <...> foo (A a, B b, estado int) {cout ... << estado; } ... auto f = [] (const char * a, float b) {foo (a, b, 42); }; '. –

+2

@Marcelo Cantos: La declaración para demostrar fue que "C++ 0x lambdas son monomórficas", precisamente porque debes-declarar el lambda con parámetros explícitamente tipados. – MSalters

+0

@MSalters: La pregunta fue (más o menos): "¿Qué es mejor?" No estoy seguro de cómo demostrar que C++ 0x lambdas son monomórficas responde la pregunta. –