Bind Vs Lambda?

C++Functional ProgrammingLambdaC++11Bind

C++ Problem Overview


I have a question about which style is preferred: std::bind Vs lambda in C++0x. I know that they serve -somehow- different purposes but lets take an example of intersecting functionality.

Using lambda:

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

Using bind:

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

Which one should we prefer? why? assuming more complex situations compared to the mentioned example. i.e. What are the advantages/disadvantages of one over the other?

C++ Solutions


Solution 1 - C++

C++0x lambdas are monomorphic, while bind can be polymorphic. You cannot have something like

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

a and b must have known types. On the other hand, tr1/boost/phoenix/lambda bind allows you to do this:

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"

Note that the types A and B are not fixed here. Only when f is actually used these two will be deduced.

Solution 2 - C++

As you said, bind and lambdas don't quite exactly aim at the same goal.

For instance, for using and composing STL algorithms, lambdas are clear winners, IMHO.

To illustrate, I remember a really funny answer, here on stack overflow, where someone asked for ideas of hex magic numbers, (like 0xDEADBEEF, 0xCAFEBABE, 0xDEADDEAD etc.) and was told that if he were a real C++ programmer he would simply have download a list of English words and use a simple one-liner of 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
    );
}

This snippet, in pure C++98, open the English words file, scan each word and print only those of length 8 with 'a', 'b', 'c', 'd', 'e' or 'f' letters.

Now, turn on C++0X and 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);
    }
 );
}

This is still a bit heavy to read (mainly because of the istream_iterator business), but a lot simpler than the bind version :)

Solution 3 - C++

The C++ 0x lamdba syntax is more readable than the bind syntax. Once you get into more than 2-3 level bind, you code becomes pretty much unreadable and difficult to maintain. I would prefer the more intuitive lambda syntax.

Solution 4 - C++

One of the benefits of lambdas is they are way more useful when you need to add a little big of logic on top of an existing function.

With bind, you are forced to create a new function/method/functor even if the logic is only ever needed in this one place. You need to come up with an appropriate name and it can make the code less understandable as it potentially makes you split up related logic.

With lambda, you can add the new logic inside the lambda (but are not forced to if it makes sense to create a new callable).

Solution 5 - C++

I think it's more a matter of taste. People that quickly grasp new technologies, or are familiar with functional programming will probably prefer lambda syntax, while more conservative programmers will definitively prefer bind, as it is more in par with the traditional C++ syntax.

Such a decision should be made in coordination with the people that will be working with the code, probably through a majority vote.

Which doesn't change the fact however, that lambda syntax is much more powerful and cleaner.

Solution 6 - C++

A key advantage of lambdas is they can reference member functions statically, while bind can only reference them through a pointer. Worse, at least in compilers that follow the "itanium c++ ABI" (e.g. g++ and clang++) a pointer to a member function is twice the size of a normal pointer.

So with g++ at least, if you do something like std::bind(&Thing::function, this) you get a result that is three pointers in size, two for the pointer to member function and one for the this pointer. On the other hand if you do [this](){function()} you get a result that is only one pointer in size.

The g++ implementation of std::function can store up to two pointers without dynamic memory allocation. So binding a member function to this and storing it in a std::function will result in dynamic memory allocation while using a lambda and capturing this will not.


From a comment:

> A member function must be at least 2 pointers because it must store a function pointer, and this, plus at least 1 more value for meta-data such as the number of arguments. The lambda is 1 pointer because it points to this data, not because it's been magicked away.

No

A "pointer to member function" is (at least under the "itanium C++ ABI", but I suspect other compilers are similar) two pointers in size, because it stores both a pointer to the actual member function (or a vtable offset for virtual member functions) and also a "this pointer adjustment" to support multiple inheritance. Binding the this pointer to the member member function results in an object three pointers in size.

With a lambda on the other hand, every lambda has a unique type, and the information on what code to run is stored as part of the type, not part of the value. Therefore only the captures need to be stored as part of the lambda's value. At least under g++ a lambda that captures a single pointer by value has the size of a single pointer.

Neither the lambda, the pointer to member function or the result of bind store the number of parameters as part of their data. That information is stored as part of their type.

The g++ implementation of a std::function is four pointers in size, it consists of a function pointer to a "caller" function, a function pointer to a "manager" function and a data area that is two pointers in size. The "invoker" function is used when a program wants to call the callable object stored in the std::function. The manager function is called when the callable object in the std::function needs to be copied, destroyed etc.

When you construct or assign to a std::function, implementations of the invoker and manager function are generated through templating. This is what allows the std::function to store arbitrary types.

If the type you assign is able to fit in the std::function's data area then g++'s implementation (and I strongly suspect most other implementations) will store it directly in there, so dynamic memory allocation is not needed.

To demonstrate why a lambda is far better than bind in this case I wrote some small test code.

struct widget
{
    void foo();
    std::function<void()> bar();  
    std::function<void()> baz();  
};

void widget::foo() {
    printf("%p",this);
}

std::function<void()> widget::bar() {
    return [this](){foo();};
}

std::function<void()> widget::baz() {
    return std::bind(&widget::foo, this);
}

I fed this into godbolt using the "armv7-a clang trunk" option with -O2 and -fno-rtti and looked at the resulting assembler. I have manually separated out the assembler for bar and baz. Lets first look at the assembler for bar.

widget::bar():
        ldr     r2, .LCPI1_0
        str     r1, [r0]
        ldr     r1, .LCPI1_1
        str     r1, [r0, #8]
        str     r2, [r0, #12]
        bx      lr
.LCPI1_0:
        .long   std::_Function_handler<void (), widget::bar()::$_0>::_M_invoke(std::_Any_data const&)
.LCPI1_1:
        .long   std::_Function_base::_Base_manager<widget::bar()::$_0>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
std::_Function_handler<void (), widget::bar()::$_0>::_M_invoke(std::_Any_data const&):
        ldr     r1, [r0]
        ldr     r0, .LCPI3_0
        b       printf
.LCPI3_0:
        .long   .L.str
std::_Function_base::_Base_manager<widget::bar()::$_0>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation):
        cmp     r2, #2
        beq     .LBB4_2
        cmp     r2, #1
        streq   r1, [r0]
        mov     r0, #0
        bx      lr
.LBB4_2:
        ldr     r1, [r1]
        str     r1, [r0]
        mov     r0, #0
        bx      lr

We see, that bar itself is very simple, it's just filling out the std::function object with the value of the this pointer and with pointers to the caller and manager functions. The "invoker" and "manager" functions are also pretty simple, there is no dynamic memory allocation in sight and the compiler has inlined foo into the "invoker" function.

Now lets look at the assembler for baz:

widget::baz():
        push    {r4, r5, r6, lr}
        mov     r6, #0
        mov     r5, r0
        mov     r4, r1
        str     r6, [r0, #8]
        mov     r0, #12
        bl      operator new(unsigned int)
        ldr     r1, .LCPI2_0
        str     r4, [r0, #8]
        str     r0, [r5]
        stm     r0, {r1, r6}
        ldr     r1, .LCPI2_1
        ldr     r0, .LCPI2_2
        str     r0, [r5, #8]
        str     r1, [r5, #12]
        pop     {r4, r5, r6, lr}
        bx      lr
.LCPI2_0:
        .long   widget::foo()
.LCPI2_1:
        .long   std::_Function_handler<void (), std::_Bind<void (widget::*(widget*))()> >::_M_invoke(std::_Any_data const&)
.LCPI2_2:
        .long   std::_Function_base::_Base_manager<std::_Bind<void (widget::*(widget*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
std::_Function_handler<void (), std::_Bind<void (widget::*(widget*))()> >::_M_invoke(std::_Any_data const&):
        ldr     r0, [r0]
        ldm     r0, {r1, r2}
        ldr     r0, [r0, #8]
        tst     r2, #1
        add     r0, r0, r2, asr #1
        ldrne   r2, [r0]
        ldrne   r1, [r2, r1]
        bx      r1
std::_Function_base::_Base_manager<std::_Bind<void (widget::*(widget*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation):
        push    {r4, r5, r11, lr}
        mov     r4, r0
        cmp     r2, #3
        beq     .LBB6_3
        mov     r5, r1
        cmp     r2, #2
        beq     .LBB6_5
        cmp     r2, #1
        ldreq   r0, [r5]
        streq   r0, [r4]
        b       .LBB6_6
.LBB6_3:
        ldr     r0, [r4]
        cmp     r0, #0
        beq     .LBB6_6
        bl      operator delete(void*)
        b       .LBB6_6
.LBB6_5:
        mov     r0, #12
        bl      operator new(unsigned int)
        ldr     r1, [r5]
        ldm     r1, {r2, r3}
        ldr     r1, [r1, #8]
        str     r0, [r4]
        stm     r0, {r2, r3}
        str     r1, [r0, #8]
.LBB6_6:
        mov     r0, #0
        pop     {r4, r5, r11, lr}
        bx      lr

We see it's worse than the code for bar in almost every respect. The code for baz itself is now over twice as long and includes dynamic memory allocation.

The invoker function can no longer inline foo or even call it directly, instead it must go through the whole rigmarole of calling a pointer to member function.

The manager function is also substantially more complex and involves dynamic memory allocation.

Solution 7 - C++

C++0x lambdas essentially replace bind. There is nothing you can bind that you can't recreate a trivial wrapper lambda to achieve the same. std::tr1::bind will go the way of std::bind1st, etc once lambda support is wide spread. Which is good, because for some reason most programmers have a hard time getting their head around bind.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKhaled AlshayaView Question on Stackoverflow
Solution 1 - C++dv_View Answer on Stackoverflow
Solution 2 - C++Thomas PetitView Answer on Stackoverflow
Solution 3 - C++pranavsharmaView Answer on Stackoverflow
Solution 4 - C++R Samuel KlatchkoView Answer on Stackoverflow
Solution 5 - C++Kornel KisielewiczView Answer on Stackoverflow
Solution 6 - C++plugwashView Answer on Stackoverflow
Solution 7 - C++Terry MahaffeyView Answer on Stackoverflow