6

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.

Paul Varghese
  • 1,635
  • 1
  • 15
  • 30
  • Considering that [`std::logical_and`](http://en.cppreference.com/w/cpp/utility/functional/logical_and) *evaluates* its arguments, and not calls them, the code you show will not work. – Some programmer dude Jul 13 '16 at 11:11
  • 3
    `bind` is very quirky when invoked recursively. Please consider just using lambdas. – Yakk - Adam Nevraumont Jul 13 '16 at 11:15
  • Still not completely understood. But from your comment I understand that bind function will call the functors internally and it's result is binded to the logical_and? not sure :( – Paul Varghese Jul 13 '16 at 11:24

1 Answers1

5

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)()
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Thanks for the detailed explanation. Now it's clear. – Paul Varghese Jul 13 '16 at 11:30
  • 2
    @PaulVarghese I love `bind` for it's clarity and for the familiarity I have with it from back in the days of `bind_1st` and `bind_2nd`. But honestly, compilers have deffered optimization of `bind` for optimization of lambdas. This is at least true of gcc: https://youtu.be/ZlHi8txU4aQ I'm not certain where Visual Studio lands on everything, but I think the general climate says prefer lambdas to `bind`s. – Jonathan Mee Jul 13 '16 at 12:13