Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

problem sorting using member function as comparator

trying to compile the following code I get this compile error, what can I do?


ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.

class MyClass {
   int * arr;
   // other member variables
   MyClass() { arr = new int[someSize]; }

   doCompare( const int & i1, const int & i2 ) { // use some member variables } 

   doSort() { std::sort(arr,arr+someSize, &doCompare); }

}; 
like image 621
Navid Avatar asked Dec 14 '09 17:12

Navid


4 Answers

doCompare must be static. If doCompare needs data from MyClass you could turn MyClass into a comparison functor by changing:

doCompare( const int & i1, const int & i2 ) { // use some member variables } 

into

bool operator () ( const int & i1, const int & i2 ) { // use some member variables } 

and calling:

doSort() { std::sort(arr, arr+someSize, *this); }

Also, isn't doSort missing a return value?

I think it should be possible to use std::mem_fun and some sort of binding to turn the member function into a free function, but the exact syntax evades me at the moment.

EDIT: Doh, std::sort takes the function by value which may be a problem. To get around this wrap the function inside the class:

class MyClass {
    struct Less {
        Less(const MyClass& c) : myClass(c) {}
        bool operator () ( const int & i1, const int & i2 ) {// use 'myClass'} 
        MyClass& myClass;
    };
    doSort() { std::sort(arr, arr+someSize, Less(*this)); }
}
like image 179
Andreas Brinck Avatar answered Nov 14 '22 05:11

Andreas Brinck


As Andreas Brinck says, doCompare must be static (+1). If you HAVE TO have a state in your comparator function (using the other members of the class) then you'd better use a functor instead of a function (and that will be faster):

class MyClass{

   // ...
   struct doCompare
   { 
       doCompare( const MyClass& info ) : m_info(info) { } // only if you really need the object state
       const MyClass& m_info;

       bool operator()( const int & i1, const int & i2  )
       { 
            // comparison code using m_info
       }
   };

    doSort() 
    { std::sort( arr, arr+someSize, doCompare(*this) ); }
};

Using a functor is always better, just longer to type (that can be unconvenient but oh well...)

I think you can also use std::bind with the member function but I'm not sure how and that wouldn't be easy to read anyway.

UPDATE 2014: Today we have access to c++11 compilers so you could use a lambda instead, the code would be shorter but have the exact same semantic.

like image 22
Klaim Avatar answered Nov 14 '22 06:11

Klaim


The solution proposed by Rob is now valid C++11 (no need for Boost):

void doSort()
{
  using namespace std::placeholders;
  std::sort(arr, arr+someSize, std::bind(&MyClass::doCompare, this, _1, _2));
}

Indeed, as mentioned by Klaim, lambdas are an option, a bit more verbose (you have to "repeat" that the arguments are ints):

void doSort()
{
  std::sort(arr, arr+someSize, [this](int l, int r) {return doCompare(l, r); });
}

C++14 supports auto here:

void doSort()
{
  std::sort(arr, arr+someSize, [this](auto l, auto r) {return doCompare(l, r); });
}

but still, you declared that arguments are passed by copy.

Then the question is "which one is the most efficient". That question was treated by Travis Gockel: Lambda vs Bind. His benchmark program gives on my computer (OS X i7)

                        Clang 3.5    GCC 4.9
   lambda                    1001        7000
   bind                3716166405  2530142000
   bound lambda        2438421993  1700834000
   boost bind          2925777511  2529615000
   boost bound lambda  2420710412  1683458000

where lambda is a lambda used directly, and lambda bound is a lambda stored in a std::function.

So it appears that lambdas are a better option, which is not too much of a surprise since the compiler is provided with higher level information from which it can make profit.

like image 11
akim Avatar answered Nov 14 '22 04:11

akim


You can use boost::bind:

void doSort() {
  std::sort(arr,arr+someSize, boost::bind(&MyClass::doCompare, this, _1, _2));
}
like image 5
Robᵩ Avatar answered Nov 14 '22 06:11

Robᵩ