Связать Vs Лямбда?
У меня вопрос о том, какой стиль предпочтительнее: std:: bind Vs lambda в С++ 0x. Я знаю, что они служат - как-то - разные цели, но позволяют взять пример пересекающихся функциональных возможностей.
Использование lambda
:
uniform_int<> distribution(1, 6);
mt19937 engine;
// lambda style
auto dice = [&]() { return distribution(engine); };
Использование bind
:
uniform_int<> distribution(1, 6);
mt19937 engine;
// bind style
auto dice = bind(distribution, engine);
Какой из них мы предпочитаем? Зачем? предполагая более сложные ситуации по сравнению с указанным примером. т.е. какие преимущества/недостатки одного над другим?
Ответы
Ответ 1
Как вы сказали, bind и лямбды не совсем точно нацелены на одну и ту же цель.
Например, для использования и составления алгоритмов STL лямбды являются явными победителями, ИМХО.
Чтобы проиллюстрировать, я помню действительно забавный ответ, здесь, в переполнении стека, где кто-то просил идеи шестизначных магических чисел (например, 0xDEADBEEF, 0xCAFEBABE, 0xDEADDEAD и т.д.), и ему сказали, что если бы он был настоящим программистом на С++, просто загрузили бы список английских слов и использовали бы простой однострочный слой С++:)
#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
);
}
Этот фрагмент, в чистом С++ 98, откройте английский файл слов, сканирует каждое слово и печатайте только те, которые имеют длину 8 с помощью "a", "b", "c", "d", "e" или Буквы 'f'.
Теперь включите С++ 0X и 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);
}
);
}
Это все еще немного тяжело читать (в основном из-за бизнеса istream_iterator), но намного проще, чем версия связывания:)
Ответ 2
С++ 0x lambdas мономорфны, а связывание может быть полиморфным. Вы не можете иметь что-то вроде
auto f = [](auto a, auto b) { cout << a << ' ' << b; }
f("test", 1.2f);
a и b должны иметь известные типы. С другой стороны, tr1/boost/phoenix/lambda bind позволяет вам сделать это:
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"
Обратите внимание, что типы A и B здесь не фиксированы. Только когда f фактически используется, эти два будут выведены.
Ответ 3
Синтаксис С++ 0x lamdba более читабельен, чем синтаксис связывания. После того, как вы переходите к связыванию более 2-3 уровней, код становится практически нечитаемым и его трудно поддерживать. Я бы предпочел более интуитивный синтаксис лямбда.
Ответ 4
Одним из преимуществ lambdas является то, что они более полезны, когда вам нужно добавить немного большую логику поверх существующей функции.
При связывании вы вынуждены создавать новую функцию/метод/функтор, даже если логика нужна только в этом месте. Вам нужно придумать соответствующее имя, и это может сделать код менее понятным, поскольку он потенциально может привести к разделению связанной логики.
С lambda вы можете добавить новую логику внутри лямбда (но не вынуждены, если имеет смысл создать новый вызываемый).
Ответ 5
Я думаю, что это скорее вопрос вкуса. Люди, которые быстро овладевают новыми технологиями или знакомы с функциональным программированием, вероятно, предпочтут лямбда-синтаксис, в то время как более консервативные программисты окончательно предпочтут связывание, поскольку оно больше соответствует традиционному синтаксису С++.
Такое решение должно приниматься в координации с людьми, которые будут работать с кодом, вероятно, с большинством голосов.
Что не меняет факт, однако, что лямбда-синтаксис намного эффективнее и чище.
Ответ 6
С++ 0x lambdas существенно заменяет bind. Нет ничего, что вы могли бы связать, чтобы вы не могли воссоздать тривиальный оберточный лямбда для достижения того же. std:: tr1:: bind будет идти по пути std:: bind1st и т.д., когда поддержка lambda широко распространена. Это хорошо, потому что по какой-то причине большинству программистов приходится с трудом ловить голову вокруг привязки.