Подтвердить что ты не робот

Связать 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);

Какой из них мы предпочитаем? Зачем? предполагая более сложные ситуации по сравнению с указанным примером. т.е. какие преимущества/недостатки одного над другим?

4b9b3361

Ответ 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 широко распространена. Это хорошо, потому что по какой-то причине большинству программистов приходится с трудом ловить голову вокруг привязки.