Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how bind function works for functional objects in c++

Tags:

c++

c++11

I came across the following find_if function.

find_if (coll.begin(), coll.end(), 
             bind(logical_and<bool>(), 
                  bind(greater<int>(),_1,x), bind(less<int>(),_1,y)
                 )
         );

I've the doubt that how the bind(greater(),_1,x) and bind(less(),_1,y) are evaluated and returning bool values there? This will not work otherwise as shown below.

#include <iostream>
#include <functional>

int main()
{
    using namespace std::placeholders;

    //auto fn = std::bind(std::greater<int>(), 5, _1);
    //std::cout << fn(7) << std::endl;
    //std::cout << typeid(fn).name() << std::endl;

    auto fn1 = std::bind(std::greater<int>(),5,6);
    auto fn2 = std::bind(std::less<int>(),7,5);

    std::cout << std::bind( std::logical_and<bool>(), fn1, fn2 )(); // how this works??
    std::cout << std::logical_and<bool>()(fn1, fn2)();  // Compilation error
}

Really curious to know how the functors are called inside the bind function. Can someone please explain how this works? Thanks in advance.

like image 774
Paul Varghese Avatar asked Jul 13 '16 11:07

Paul Varghese


1 Answers

To understand this we'll need to 1st understand how bind, binds it's arguments. Given that g is the result of a bind expression which is called with: g(u1, u2, ... uM):

  • If the stored argument arg is of type std::reference_wrapper<T> (for example, std::ref or std::cref was used in the initial call to bind), then the argument vn in the std::invoke call above is arg.get() and the type Vn in the same call is T&: the stored argument is passed by reference into the invoked function object.
  • If the stored argument arg is of type T for which std::is_bind_expression<T>::value == true (meaning, 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>(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).
  • If the stored argument arg is of type T, for which std::is_placeholder<T>::value != 0, meaning, a placeholder such as std::placeholders::_1, _2, _3, ... was used as the argument to the initial call to bind), then the argument indicated by the placeholder (u1 for _1, u2 for _2, etc) is passed to the invokable object: the argument vn in the std::invoke call above is std::forward<Uj>(uj) and the corresponding type Vn in the same call is Uj&&.
  • Otherwise, the ordinary stored argument arg is passed to the invokable object as lvalue argument: the argument vn in the std::invoke call above is simply arg and the corresponding type Vn is T cv &, where cv is the same cv-qualification as that of g.

The key is in the 2nd bullet. Because the bind expressions are invoked at binding time this works:

std::cout << std::bind(std::logical_and<bool>(), fn1, fn2)()

But because there is no & operator defined for bind expressions, this won't work:

std::cout << std::logical_and<bool>()(fn1, fn2)()
like image 129
Jonathan Mee Avatar answered Sep 20 '22 08:09

Jonathan Mee