J'ai une question sur le style préféré: std :: bind Vs lambda en C++ 0x. Je sais qu'ils servent à des fins différentes, mais prenons un exemple de fonctionnalité entrecroisée.
Utilisation de lambda
:
uniform_int<> distribution(1, 6);
mt19937 engine;
// lambda style
auto dice = [&]() { return distribution(engine); };
Utilisation de bind
:
uniform_int<> distribution(1, 6);
mt19937 engine;
// bind style
auto dice = bind(distribution, engine);
Lequel devrions-nous préférer? Pourquoi? en supposant des situations plus complexes par rapport à l'exemple mentionné. c'est-à-dire quels sont les avantages/inconvénients de l'un par rapport à l'autre?
Comme vous l'avez dit, bind et lambdas ne visent pas exactement le même objectif.
Par exemple, pour l'utilisation et la composition d'algorithmes STL, les lambdas sont clairement gagnants, à mon humble avis.
Pour illustrer, je me souviens d'une réponse vraiment drôle, ici sur le débordement de pile, où quelqu'un a demandé des idées de nombres magiques hexadécimaux (comme 0xDEADBEEF, 0xCAFEBABE, 0xDEADDEAD etc.) et on m'a dit que s'il était un vrai programmeur C++, il aurait simplement téléchargez une liste de mots anglais et utilisez une simple ligne 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
);
}
Cet extrait, en C++ 98 pur, ouvre le fichier de mots anglais, numérise chaque mot et n'imprime que ceux de longueur 8 avec "a", "b", "c", "d", "e" ou "f" des lettres.
Maintenant, activez C++ 0X et 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);
}
);
}
C'est encore un peu lourd à lire (principalement à cause de l'activité istream_iterator), mais beaucoup plus simple que la version bind :)
Les lambdas C++ 0x sont monomorphes, tandis que la liaison peut être polymorphe. Vous ne pouvez pas avoir quelque chose comme
auto f = [](auto a, auto b) { cout << a << ' ' << b; }
f("test", 1.2f);
a et b doivent avoir des types connus. En revanche, tr1/boost/phoenix/lambda bind vous permet de faire ceci:
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"
Notez que les types A et B sont pas fixes ici. Ce n'est que lorsque f est réellement utilisé que ces deux seront déduits.
La syntaxe lamdba C++ 0x est plus lisible que la syntaxe de liaison. Une fois que vous entrez dans plus de 2-3 niveaux de liaison, votre code devient à peu près illisible et difficile à maintenir. Je préférerais la syntaxe lambda plus intuitive.
L'un des avantages des lambdas est qu'ils sont bien plus utiles lorsque vous devez ajouter un peu de logique en plus d'une fonction existante.
Avec bind, vous êtes obligé de créer une nouvelle fonction/méthode/foncteur même si la logique n'est nécessaire que dans ce seul endroit. Vous devez trouver un nom approprié et cela peut rendre le code moins compréhensible car il vous fait potentiellement diviser la logique associée.
Avec lambda, vous pouvez ajouter la nouvelle logique à l'intérieur du lambda (mais n'y êtes pas obligé s'il est logique de créer un nouvel appelable).
Je pense que c'est plus une question de goût. Les personnes qui maîtrisent rapidement les nouvelles technologies ou qui sont familières avec la programmation fonctionnelle préféreront probablement la syntaxe lambda, tandis que les programmeurs plus conservateurs préféreront définitivement bind, car cela correspond davantage à la syntaxe C++ traditionnelle.
Une telle décision devrait être prise en coordination avec les personnes qui travailleront avec le code, probablement par un vote majoritaire.
Ce qui ne change pas le fait cependant, que la syntaxe lambda est beaucoup plus puissante et plus propre.
Les lambdas C++ 0x remplacent essentiellement la liaison. Il n'y a rien que vous puissiez lier que vous ne puissiez pas recréer un lambda wrapper trivial pour obtenir le même résultat. std :: tr1 :: bind suivra le chemin de std :: bind1st, etc une fois que le support lambda sera largement répandu. Ce qui est bien, car pour une raison quelconque, la plupart des programmeurs ont du mal à se concentrer.