2009-12-14 9 views
22

tratando de compilar el siguiente código Obtengo este error de compilación, ¿qué puedo hacer?clasificación de problemas usando la función de miembro como comparador


ISO C++ prohíbe tomar la dirección de una función miembro no estática no cualificado o 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); } 

}; 
+0

Duplicado exacto de http://stackoverflow.com/q/639100/627163; sin embargo, esto aquí se plantea de una manera mucho más sucinta. – Daniel

Respuesta

25

doCompare debe haber static. Si doCompare necesidades de datos de MyClass se podrían convertir en un funtor MyClass comparación cambiando:

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

en

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

y llamando:

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

Además, no es la falta de un doSort valor de retorno?

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

EDITAR: Doh, std::sort toma el functor por valor que puede ser un problema. Para evitar esto envolver el 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)); } 
} 
+1

Todavía hay un problema en esta solución. Tipo de STL llamado distructor del objeto que se le pasa como comparador, ¡esto arruinaría mi programa! – Navid

+1

@Navid Mira mi respuesta editada. –

13

Como dice Andreas Brinck, doCompare debe ser estático (1). Si usted tiene que tener un estado en su función de comparación (utilizando los otros miembros de la clase), entonces es mejor utilizar un funtor en lugar de una función (y que será más rápida):

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

El uso de un funtor 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 ser fácil de leer de todos modos.

ACTUALIZACIÓN 2014: Hoy tenemos acceso a los compiladores de C++ 11 por lo que podría utilizar una lambda en su lugar, el código sería más corto pero con la misma semántica exacta.

+0

Finalmente encontré una explicación sensata de cómo hacer esto ...! Gracias. – porgarmingduod

+0

Muy bueno, y esto se puede adaptar fácilmente a una solución genérica que toma una clase ptr + method ptr. – tenfour

2

Hay una manera de hacer lo que quiera, pero debe usar un adaptador pequeño.A medida que el STL no escribe por usted, puede usted mismo puede escribir:

template <class Base, class T> 
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 <class Base, class T> 
adaptor_t<Base,T> adapt_method(Base* b, typename adaptor_t<Base,T>::method_t m) 
{ return adaptor_t<Base,T>(b,m); } 

A continuación, se puede usar:

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

Usted puede utilizar boost::bind:

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

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

En efecto, como se ha mencionado por Klaim, lambdas son una opción, un poco más de detalle (que tiene que "repetir" que los argumentos son enteros):

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

C++ 14 soporta auto aquí:

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

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

Entonces la pregunta es "cuál es la más eficiente". Esa pregunta fue tratada por Travis Gockel: Lambda vs Bind. Su programa de referencia da en mi equipo (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 un lambda utilizar directamente, y lambda bound es un lambda almacenado en un std::function.

Parece ser que las lambdas son una mejor opción, lo que no es una gran sorpresa ya que el compilador cuenta con información de mayor nivel de la que puede obtener ganancias.

+0

el segundo y el tercer ejemplo deberían ir sin usando namespace std :: placeholders; –

+0

@RuslanZasukhin Gracias, corregido. – akim

0

Una manera muy simple de usar efectivamente una función miembro es usar el operador <. Es decir, si tiene una función llamada comparar, puede llamarla desde el operador <. Aquí está 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; 
}; 

A continuación, usted ni siquiera necesita dar el nombre de la función std :: sort:

std::vector<Qaz> 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()); 
0

Actualización de respuesta Graham Asher, ya que no es necesario el comparar pero puede usar el operador menos directamente.

#include <iostream> 
#include <vector> 
#include <algorithm> 

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<Qaz> 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; 
} 
Cuestiones relacionadas