c++ - instruccion - clasificación de problemas utilizando la función de miembro como comparador
switch en c# consola (7)
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;
}
tratando de compilar el siguiente código, obtengo este error de compilació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); }
};
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 compiladores 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.
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 <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)); }
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 programa 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 compilador 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));
}
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<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());
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 sintaxis 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)); }
}