4

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)?

JeJo
  • 30,635
  • 6
  • 49
  • 88
fabiop
  • 179
  • 10

2 Answers2

3

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
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • For the nested bind expression std::bind(std::plus(), std::placeholders::_1, 10) , it is the CALL operator which returns an integer, but who calls it? std;;bind do not simply copy-construct its binded parameters from the arguments? – fabiop Aug 28 '20 at 05:48
0

Below an extract from cppreference.com about std::bind:

"Given an object g obtained from an earlier call to bind, when it is invoked in a function call expression g(u1, u2, ... uM), an invocation of the stored object takes place, as if by std::invoke(fd, std::forward(v1), std::forward(v2), ..., std::forward(vN)), where fd is a value of type std::decay_t the values and types of the bound arguments v1, v2, ..., vN are determined as specified below.

...

If the stored argument arg is of type T for which std::is_bind_expression::value == true (for example, another bind expression was passed directly into the initial call to bind), then bind performs function composition: instead of passing the function object that the bind subexpression would return, the subexpression is invoked eagerly, and its return value is passed to the outer invokable object. If the bind subexpression has any placeholder arguments, they are shared with the outer bind (picked out of u1, u2, ...). Specifically, the argument vn in the std::invoke call above is arg(std::forward(uj)...) and the type Vn in the same call is std::result_of_t<T cv &(Uj&&...)>&& (cv qualification is the same as that of g)."

So, the key aspect is that i was passing an argument for which std::is_bind_expression<T>::value == true, and that modifies "normal" std::bind behaviour

fabiop
  • 179
  • 10