Home > Mobile >  How can std::bind bind a binary operation of two different-type parameters passed in to std::sort?
How can std::bind bind a binary operation of two different-type parameters passed in to std::sort?

Time:10-09

In this example I am supposed to sort a vector of strings depending on a given size, lest's say elements whose length greater than or equal to the given size come first and then the remaining.

So here is what I've done:

bool is_shorter(std::string const& str, std::string::size_type sz){
    return !(str.size() < sz);
}

int main(){

    std::vector<string> vstr{"vector", "In", "this", "example", "am", "supposed",
      "sort", "a", "of", "I", "strings", "depending"};

    std::string::size_type len = 5;
//    std::sort( vstr.begin(), vstr.end(),
//        std::bind(is_shorter, placeholders::_1, len) );

    std::sort( vstr.begin(), vstr.end(),
        std::bind( [](std::string const& str, std::string::size_type sz){
            return !(str.size() < sz);}, std::placeholders::_1, len ) );

    for(auto const& str : vstr)
        std::cout << str << ", ";
    std::cout << '\n';


    std::cout << '\n';
}

The output:

depending, strings, supposed, example, vector, In, this, am, sort, a, of, I,
  • As you can see the program works fine as I expected but what matters and confuses me is how come the comparison operation:

  • Inside sort I guess something like this:

      comp(*it, *it   1); // for simplicity sake only. (I mean two elements of the same sequence -of course same type- are passed-in)
    

Which I mean the comparison operation is a binary operation that is called each iteration for each contiguous elements. Those elements are of the same type (of the elements in the sequence).

  • So how can std::bind changes the second parameter to std::string::size_type rather than std::string?

  • How can std::bind makes the operation takes only one string? and what happens to the second string passed in?

  • Can someone explain to me what happens exactly in std::bind here?

It doesn't matter me if the lambda passed-in or the custom comparison operation if it takes arguments of the same type:

for example to sort a vector of strings so that the elements that are equal to a given string appear first:

  std::sort(vstr.begin(), vstr.end(),
        std::bind([](std::string s1, std::string s2 ){ return s1 == s2;}, _1, std::string("vector")
    ));
  • Now If I run the program I get the word "vector" appearing first at the beginning.

CodePudding user response:

The thing is, you don't actually want to sort the array. (You could do it like that, by writing a comparator which returned true if the first string were shorter than the limit and the second was longer than the limit, and false otherwise, but it would be inefficient and complicated.)

What you want is std::partition, which takes a unary predicate, and reorders the array so that elements which the predicate return true for are moved to the front of the array, and the ones where the predicate returns false are moved to the end of the array.

Once you're working with an algorithm which takes a unary predicate, your current partial-binding approach will work fine.

CodePudding user response:

Not exactly related, but your answer is wrong: "depending, strings, supposed, example, vector, In, this, am, sort, a, of, I," Note that "In" is before "this" and "sort". Your sorting function should be comparing the sizes of the two input strings.

Now, for the 'How does bind work?'. Basically, when you call std::bind, it copies/moves all of the additional arguments provided into the returned data structure. This structure has a templated operator(). The parameters passed to operator() are substituted in place of the std::placeholders objects that were provided during the call to bind. The important part here is that any parameters passed during the invocation that are not mentioned are not forwarded to the bound functor.

So in this case, because you didn't pass std::placeholders:_2 to the call to bind, the second argument is discarded. The second argument to the lambda is filled in by len which you passed in.

There is more functionality in std::bind, but this covers the simplest use case.

  • Related