2009-12-14 9 views
22

cercando di compilare il codice seguente ottengo questo errore di compilazione, cosa posso fare?problema di ordinamento utilizzando la funzione membro come comparatore


ISO C++ proibisce prendendo l'indirizzo di una funzione membro static qualificato o tra parentesi a formare un puntatore alla funzione membro.

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

duplicato esatto di http://stackoverflow.com/q/639100/627163; tuttavia, questo qui è presentato in un modo molto più succinto. – Daniel

risposta

25

doCompare deve essere static. Se doCompare ha bisogno di dati da MyClass si potrebbe trasformare in un MyClass funtore confronto modificando:

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

in

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

e chiamando:

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

Inoltre, non manca un doSort valore di ritorno?

Penso che dovrebbe essere possibile utilizzare std::mem_fun e una sorta di associazione per trasformare la funzione membro in una funzione libera, ma la sintassi esatta mi sfugge al momento.

EDIT: Doh, std::sort prende il functor dal valore che può essere un problema. Per ovviare a questo avvolgere il funtore all'interno della classe:

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

C'è ancora un problema in questa soluzione. L'ordinamento STL ha chiamato il distruttore dell'oggetto che è passato ad esso come comparatore, questo rovinerebbe il mio programma !!! – Navid

+1

@Navid Guarda la mia risposta modificata. –

13

Come dice Andreas Brinck, doCompare deve essere statico (+1). Se si deve avere uno stato nella funzione di confronto (con gli altri membri della classe), allora è meglio utilizzare un funtore al posto di una funzione (e che sarà più veloce):

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

Utilizzando un funtore è sempre meglio, appena più lungo da digitare (che può essere scomodo ma vabbè ...)

Penso che si possa usare anche std :: bind con la funzione membro, ma non sono sicuro di come e cosa no essere facile da leggere comunque.

AGGIORNAMENTO 2014: Oggi abbiamo accesso ai compilatori C++ 11 in modo da poter utilizzare invece una lambda, il codice sarebbe più corto ma avere la stessa identica semantica.

+0

Finalmente ho trovato una spiegazione ragionevole su come fare questo ..! Grazie. – porgarmingduod

+0

Molto bello - e questo può essere facilmente adattato ad una soluzione generica che accetta un metodo ptr + ptr di classe. – tenfour

2

C'è un modo per fare ciò che si desidera, ma è necessario utilizzare un piccolo adattatore.Come lo STL non scrive per voi, può può scrivere da soli:

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

Poi, si può utilizzare:

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

È possibile utilizzare boost::bind:

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

La soluzione proposta da Rob è ora valida C++ 11 (non necessario per Boost):

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

Infatti, come detto da Klaim, lambda sono un'opzione, un po 'più dettagliato (si deve "ripetere" che gli argomenti sono interi):

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

C++ 14 supporta auto qui:

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

ma ancora, avete dichiarato che gli argomenti sono passati per copia.

Quindi la domanda è "quale è il più efficiente". Questa domanda è stata trattata da Travis Gockel: Lambda vs Bind. Il suo programma di benchmark dà sul mio 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 

dove lambda è una lambda utilizzato direttamente, e lambda bound è una lambda memorizzato in un std::function.

Quindi sembra che i lambda siano un'opzione migliore, il che non è una sorpresa, dal momento che al compilatore vengono fornite informazioni di livello superiore da cui può trarre profitto.

+0

il secondo e il terzo esempio dovrebbero andare senza usando namespace std :: segnaposti; –

+0

@RuslanZasukhin Grazie, risolto. – akim

0

Un modo molto semplice per utilizzare in modo efficace una funzione membro è utilizzare l'operatore <. Cioè, se hai una funzione chiamata compare, puoi chiamarla dall'operatore <. Ecco un esempio di lavoro:

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

Allora non c'è nemmeno bisogno di dare il nome della funzione 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()); 
0

Aggiornamento Graham Asher risposta, come non è necessario il confronta ma puoi usare meno operatore direttamente.

#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; 
} 
Problemi correlati