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

5 minutos de lectura

tratando de compilar el siguiente código me sale este error de compilación, ¿qué puedo hacer?


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

}; 

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

    – Daniel

    23 de noviembre de 2012 a las 20:03

clasificacion de problemas usando la funcion miembro como comparador
Andreas Brick

doCompare debe ser static. Si doCompare necesita datos de MyClass podrías convertir MyClass en un funtor de comparación cambiando:

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 es doSort falta 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 sintaxis exacta se me escapa en este momento.

EDITAR: doh, std::sort toma la función por valor, lo que puede ser un problema. Para evitar esto, ajuste la función 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)); }
}

  • Todavía hay un problema en esta solución. La ordenación STL llamó al destructor del objeto que se le pasa como comparador, ¡esto arruinaría mi programa!

    – Navid

    14 de diciembre de 2009 a las 20:06

clasificacion de problemas usando la funcion miembro como comparador
Klaim

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 funtor 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 funtor siempre es mejor, solo que más tiempo para escribir (eso puede ser inconveniente pero bueno…)

Creo que también puede usar std::bind con la función 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 los compiladores c++ 11, por lo que podría usar una lambda en su lugar, el código sería más corto pero tendría exactamente la misma semántica.

  • ¡Finalmente encontré una explicación sensata de cómo hacer esto …! Gracias.

    – porgarmingduod

    30 de marzo de 2011 a las 7:15

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

    – diez cuatro

    28 de octubre de 2011 a las 14:39

clasificacion de problemas usando la funcion miembro como comparador
Akim

La solución propuesta por Rob ahora es válida en 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 detallada (tienes 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 admite 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 el más eficiente”. Esa pregunta fue tratada por Travis Gockel: Lambda contra enlace. Su programa de referencia 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 usada directamente, y lambda bound es una lambda almacenada en un std::function.

Por lo tanto, parece 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.

Hay una manera de hacer lo que quieras, pero necesitas usar un pequeño adaptador. Como STL no lo escribe por usted, puede escribirlo usted mismo:

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

Entonces, puedes usarlo:

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

El tercer argumento en el llamado de std::sort() no es compatible con el puntero de función que necesita std::sort(). Consulte mi respuesta a otra pregunta para obtener una explicación detallada de por qué la firma de una función miembro es diferente de una firma de función normal.

1646973847 41 clasificacion de problemas usando la funcion miembro como comparador
graham aser

Una forma muy sencilla de usar de manera efectiva una función miembro es usar operator<. Es decir, si tiene una función llamada compare, puede llamarla desde operator<. 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<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());

1646973848 571 clasificacion de problemas usando la funcion miembro como comparador
sur

Actualizando la respuesta de Graham Asher, ya que no necesita la comparación 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;
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad