ryyst
ryyst

Reputation: 9791

C++11 - binding sort function

I'd like to save myself some typing and therefore define something like this:

using namespace std;

vector<MyClass> vec;

auto vecsort = bind(sort, vec.begin(), vec.end(), [] (MyClass const &a, MyClass const &b) {
        // custom comparison function
    });

vecsort(); // I want to use vecsort() a lot afterwards

For some reason this doesn't compile - why?

Using boost is not an option.

Minimal working example:

#include <vector>
#include <utility>
#include <algorithm>
#include <functional>

using namespace std;

int main() {

    vector<pair<int, int>> vec;
    for (int i = 0; i < 10; i++)
        vec.push_back(make_pair(10 - i, 0));

    auto vecsort = bind(sort, vec.begin(), vec.end(), [] (pair<int, int> const &a, pair<int, int> const &b) {
            return a.first < b.first;
        });

    vecsort();

}

Error:

error: no matching function for call to 'bind(<unresolved overloaded function type>, std::vector<std::pair<int, int> >::iterator, std::vector<std::pair<int, int> >::iterator, main()::__lambda0)'

Upvotes: 10

Views: 887

Answers (3)

James
James

Reputation: 1804

Others have mentioned why it doesn't compile, but does this alternate solution work for you? This uses another lambda, instead of bind, to create the std::function.

#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
#include <iostream>

using namespace std;

int main() {

    vector<pair<int, int>> vec;
    for (int i = 0; i < 10; i++) {
        vec.push_back(make_pair(10 - i, 0));
    }

     auto vecsort = [&vec] {
        sort(vec.begin(), vec.end(), 
        [] (pair<int, int> const &a, pair<int, int> const &b) {
            return a.first < b.first;
        });
     };

    // vecsort will work as long as vec is in scope.
    // vecsort will modify the original vector.
    vecsort();
    for (auto i : vec) {
        std::cout << '(' << i.first << ", " << i.second << ") ";
    }
    std::cout << endl;

    vec.push_back(make_pair(-42, 0));
    vecsort();
    for (auto i : vec) {
        std::cout << '(' << i.first << ", " << i.second << ") ";
    }
    std::cout << endl;
}

Output:

(1, 0) (2, 0) (3, 0) (4, 0) (5, 0) (6, 0) (7, 0) (8, 0) (9, 0) (10, 0)
(-42, 0) (1, 0) (2, 0) (3, 0) (4, 0) (5, 0) (6, 0) (7, 0) (8, 0) (9, 0) (10, 0)

See it run here: http://ideone.com/W2YQKW

Upvotes: 7

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275500

Here is a helpful macro for this problem. What it does is create an anonymous struct whose instances represent the set of overloads of calling a particular function name (by string value) in a particular context.

C++ lacks such functionality sadly. If only someone would propose it to the C++ standardization committee. Alas.1

#define OVERLOAD_SET(FUNCTION_NAME) struct { template<typename... Args>\
  auto operator()(Args&&... args) const->\
    decltype(FUNCTION_NAME(std::forward<Args>(args)...))\
  {  return (FUNCTION_NAME(std::forward<Args>(args)...));  } \
}

In global scope:

OVERLOAD_SET( std::sort ) sorter;

Then, at point of use:

std::bind( sorter, vec.begin(), vec.end(), [](...) {...} );

The problem is that std::sort is a template (or possibly a set of templates chosen via overloading!) that generates functions when called, not an actual function. It looks like a function, but it is not. And templates and overload sets cannot be passed directly to other functions.

As an aside, an improvement to the OVERLOAD_SET macro above can be made by adding a template<typename R, typename... Args> operator R(*)(Args...)() const which allows the OVERLOAD_SET to be cast implicitly to a particular function pointer, but this is beyond the scope of this problem.


1 Xeo has made such a proposal.

Upvotes: 3

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153840

The problem is that std::sort isn't a function object. It is a function template. The easiest way to deal with the issue is to create a simple wrapper object:

struct sorter {
    template <typename RndIt, typename Cmp>
    void operator()(RndIt begin, RndIt end, Cmp cmp) {
        std::sort(begin, end, cmp);
    }
};

Now you can use

std::bind(sorter(), vec.begin(), vec.end(), [](...){ ... });

Upvotes: 12

Related Questions