clasificación de problemas utilizando la función de miembro como comparador

tratando de comstackr el siguiente código, obtengo este error de comstackción, ¿qué puedo hacer?


ISO C ++ prohíbe tomar la dirección de una función miembro no cualificada o no entre paréntesis para formar un puntero a la función miembro.

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); } }; 

doCompare debe ser static . Si doCompare necesita datos de MyClass , puede convertir MyClass en un functor de comparación al cambiar:

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

dentro

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

y llamando

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

Además, ¿no doSort falta a doSort un valor de retorno?

Creo que debería ser posible usar std::mem_fun y algún tipo de enlace para convertir la función miembro en una función libre, pero la syntax exacta me evade en este momento.

EDITAR: Doh, std::sort toma el functor por valor, lo que puede ser un problema. Para evitar esto, envuelve al funtor dentro de la clase:

 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)); } } 

Como dice Andreas Brinck, doCompare debe ser estático (+1). Si TIENE que tener un estado en su función de comparación (usando los otros miembros de la clase) entonces será mejor que use un functor en lugar de una función (y eso será más rápido):

 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) ); } }; 

Usar un functor siempre es mejor, solo más tiempo para escribir (que puede ser poco convencional, pero bueno …)

Creo que también puedes usar std :: bind con la función de miembro, pero no estoy seguro de cómo y eso no sería fácil de leer de todos modos.

ACTUALIZACIÓN 2014: Hoy tenemos acceso a comstackdores de C ++ 11 para que pueda usar una lambda en su lugar, el código sería más corto pero con la misma semántica exacta.

La solución propuesta por Rob ahora es válida C ++ 11 (sin necesidad de Boost):

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

De hecho, como lo menciona Klaim, las lambdas son una opción, un poco más prolija (hay que “repetir” que los argumentos son ints):

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

C ++ 14 admite auto aquí:

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

pero aún así, declaraste que los argumentos se pasan por copia.

Entonces la pregunta es “cuál es el más eficiente”. Esa pregunta fue tratada por Travis Gockel: Lambda vs Bind . Su progtwig de referencia lo da en mi computadora (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 

donde lambda es una lambda utilizada directamente, y lambda bound es una lambda almacenada en una std::function .

Por lo tanto, parece que las lambdas son una mejor opción, lo cual no es una gran sorpresa, ya que el comstackdor cuenta con información de mayor nivel de la que puede obtener ganancias.

Puedes usar boost::bind :

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

Hay una manera de hacer lo que quiere, pero necesita usar un adaptador pequeño. Como el STL no lo escribe para usted, puede escribirlo usted mismo:

 template  struct adaptor_t { typedef bool (Base::*method_t)(const T& t1, const T& t2)); adaptor_t(Base* b, method_t m) : base(b), method(m) {} adaptor_t(const adaptor_t& copy) : base(copy.base), method(copy.method) {} bool operator()(const T& t1, const T& t2) const { return (base->*method)(t1, t2); } Base *base; method_t method; } template  adaptor_t adapt_method(Base* b, typename adaptor_t::method_t m) { return adaptor_t(b,m); } 

Entonces, puedes usarlo:

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

Una forma muy simple de usar efectivamente una función miembro es usar operator <. Es decir, si tiene una función llamada compare, puede llamarla desde el operador <. Aquí hay un ejemplo de trabajo:

 class Qaz { public: Qaz(int aX): x(aX) { } bool operator<(const Qaz& aOther) const { return compare(*this,aOther); } static bool compare(const Qaz& aP,const Qaz& aQ) { return aP.x < aQ.x; } int x; }; 

Entonces ni siquiera necesita dar el nombre de la función a std :: sort:

 std::vector q; q.emplace_back(8); q.emplace_back(1); q.emplace_back(4); q.emplace_back(7); q.emplace_back(6); q.emplace_back(0); q.emplace_back(3); std::sort(q.begin(),q.end()); 

Actualizando la respuesta de Graham Asher, ya que no necesita la comparación, pero puede usar el operador menos directamente.

 #include  #include  #include  using namespace std; class Qaz { public: Qaz(int aX): x(aX) { } bool operator<(const Qaz& aOther) const { return x < aOther.x; } int x; }; int main() { std::vector q; q.emplace_back(8); q.emplace_back(1); q.emplace_back(4); q.emplace_back(7); q.emplace_back(6); q.emplace_back(0); q.emplace_back(3); std::sort(q.begin(),q.end()); for (auto& num : q) std::cout << num.x << "\n"; char c; std::cin >> c; return 0; }