Search code examples
c++functionc++11stdbindcallable-object

Functional composition with std::bind


I am reading the second edition of the beautiful book of Nicolai Josuttis on C++11 STL.

I found the following piece of code:

#include <functional>
#include <iostream>

int main()
{
   auto plus10 = std::bind(std::plus<int>(),
      std::placeholders::_1,
      10);
   std::cout << "+10:    " << plus10(7) << std::endl;

   auto plus10times2 = std::bind(std::multiplies<int>(),
      std::bind(std::plus<int>(),
         std::placeholders::_1,
         10),
      2);
   // ...
}

I am not able to understand how the bind object "plus10times2" works. It should not bind to int parameters?

How can it bind another bind object? How it works when the call operator of plus10times2 is called (like plus10times2(7) for example)?


Solution

  • It is called nested binding (i.e. std::bind). A std::bind call has one or more std::binds.

    In that case, it works as follows:

    First, invoke the nested std::bind expression, meaning this

    std::bind(std::plus<int>(), std::placeholders::_1, 10) 
    

    with the first argument to the plus10times2, and pass to the outer one: i.e.

    std::bind(std::multiplies<int>(), /* result of nested bind */, 2);
    

    Since the nested bind expression (i.e. inner std::bind(std::plus<int>() ...) returns an integer, and the outer one (i.e. std::bind(std::multiplies<int>()...) expects an intger in that place, it works perfectly.

    For more readings: Nested bind expressions


    That being said, since C++11, we also have lambda function. Writing the nested bind expression to equalent in lambda call might clear things out:

    const auto plus10_lmda = [](int arg) { return std::plus{}(arg, 10); };
    const auto plus10times2_lmda = [](auto callblePlus, int innerArg) { return std::multiplies{}(callblePlus(innerArg), 2); };
    std::cout << plus10times2_lmda(plus10_lmda, 7);   // prints 34
    

    or completely nested and immediately invoking the inner lambda

    const auto plus10times2_lmda = [](int innerArg) { 
       return std::multiplies{}(
          [](int arg) { return std::plus{}(arg, 10); }(innerArg) // invoke the inner one immediately
          , 2); 
    };
    std::cout << plus10times2_lmda(7);  // prints 34