4

I am writing a code to solve the following problem: Given a set of numbers x[0], x[1], ..., x[N-1], find the permutation that makes them sorted in the ascending order. In the other words, I would like to find a permutation on {0,2,...,N-1} such as i[0], i[1], ..., i[N-1] such that x[i[0]] <= x[i[1]] <= ... <= x[i[N-1]].

For this, I have stored the x vector and an index vector i (initially filled with i[j] = j) as private members of a class. I have also defined a private method as

bool MyClass::compare(size_t s, size_t t) {
    return (x[s] < x[t]);
}

Now, I would call the std::sort as follows

std::sort(i.begin(), i.end(), compare);

and I expect to get the desired result. But the code does not compile and I get the following error:

error: no matching function for call to ‘sort(std::vector<long unsigned int>::iterator, std::vector<long unsigned int>::iterator, <unresolved overloaded function type>)’

I must have done everything correctly as also the documentation of std::sort mentions that I can pass a function as the compare operator to std::sort (http://www.cplusplus.com/reference/algorithm/sort/)

Thanks for all the helps in advance.

MikeL
  • 2,369
  • 2
  • 24
  • 38

4 Answers4

9

There are a couple of issues with your approach. The first one and most evident is that you cannot use a member function as a free function. To be able to call compare you need an object of type MyClass and two integers. Inside std::sort the implementation is going to try to call a free (non-member) function with only two integer arguments.

Other than that, you cannot create a pointer to a member function without explicitly taking its address. The line std::sort(..., compare); will not compile for a member function. While non-member functions will automatically decay to a pointer to the function that is not the case here.

In C++11 there are two different solutions that you can take. The most generic is creating a lambda that captures the this argument:

std::sort(std::begin(i),std::end(i),
          [](int x, int y) { return compare(x,y); }); // or maybe even implement here

A different approach would be binding the object and the member function into a functor:

std::sort(std::begin(i),std::end(i),
          std::bind(&MyClass::compare,this,_1,_2));

In this last case, the std::bind function will create an object that implements operator() taking two arguments and will call the member function MyClass::compare on the object pointed by this.

The semantics of both approaches are slightly different, but in this case you can use either one.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thank you very much for detailed info. So, both of the approaches only work on C++11 as you mentioned. Is there any way to do it such that the code can be compiled also with the older versions of C++ compiler? (I guess the second one should be doable if I use `boost::bind` instead of `std::bind`) – MikeL Aug 16 '13 at 13:22
  • @ManiBastaniParizi: Yes, `boost::bind` is an alternative to the second. If you have a single use of this, and you really need to make it work without C++11 or `boost::bind` you could write a small functor that stores a pointer to the object, takes the two arguments and dispatches to the appropriate function (manual implementation of the lambda). – David Rodríguez - dribeas Aug 16 '13 at 13:23
1

Please keep in mind instance methods have an implicit first parameter - the this pointer of the object. Thus your comparison operator is not of the type expected by std::sort - it takes three arguments instead of the expected 2. Use a bind function to get around this(for instance boost::bind). Take a look at this question for instance.

Community
  • 1
  • 1
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • Thanks for your help. So the implicit `this` pointer is the first argument or the last? Also isn't it possible to use `std::bind` instead of `boost::bind`? – MikeL Aug 16 '13 at 13:15
  • Yes it is possible - that is why I said **a** bind function. And the this **pointer** is implicit first argument for instance methods. – Ivaylo Strandjev Aug 16 '13 at 13:16
  • @ManiBastaniParizi: Note that whether the `this` pointer is the first or last (or anywhere in between) is implementation defined [although all implementations I know make it the first] and quite unimportant given that you cannot just call the member function passing a pointer in the appropriate location. Other than unrolling your own assembly code, the order of arguments is irrelevant. – David Rodríguez - dribeas Aug 16 '13 at 13:25
0

One way round this problem is to define a function call operator in your class (see here):

bool MyClass::operator() (size_t s, size_t t) {
    return (x[s] < x[t]);
}

Then you can invoke sort() method like this:

sort(i.begin(), i.end(), *this);
Community
  • 1
  • 1
cpp
  • 3,743
  • 3
  • 24
  • 38
  • 2
    This can be quite expensive, the comparator is passed by value. – jrok Aug 16 '13 at 13:16
  • 3
    While this will make it compile, it involves creating (at least one) copy of `*this`, which might be expensive. If this approach was to be taken in C++11, you could pass `std::ref(*this)` instead of `*this` to get cheaper copies (In C++03 you can use boost for this also) – David Rodríguez - dribeas Aug 16 '13 at 13:18
0

I just want to comment that the answer of @dribeas didn't work for me. I'm using the lambda function, and I had to edit it:

 std::sort(MyVector.begin(), MyVector.end(),
      [this](int x, int y) { return compare(x,y); });
Quarktum
  • 669
  • 1
  • 8
  • 26