2009-12-14 7 views
22

en essayant de compiler le code suivant Je reçois cette erreur de compilation, que puis-je faire?problème de tri en utilisant la fonction membre comme comparateur


ISO C++ interdit de prendre l'adresse d'un qualifié ou entre parenthèses fonction membre non-statique pour former un pointeur à la fonction de membre.

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

Exemplaire exact de http://stackoverflow.com/q/639100/627163; Cependant, ceci est posé de manière beaucoup plus succincte. – Daniel

Répondre

25

doCompare doit être static. Si vous pouviez tourner MyClass dans un foncteur comparision doCompare données sur les besoins de MyClass en changeant:

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

dans

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

et appelant:

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

Aussi, n'est pas doSort manquer un retourner la valeur?

Je pense qu'il devrait être possible d'utiliser std::mem_fun et une sorte de liaison pour transformer la fonction membre en une fonction gratuite, mais la syntaxe exacte m'échappe pour le moment.

EDIT: Doh, std::sort prend le foncteur en valeur, ce qui peut poser un problème. Pour contourner ce problème envelopper le foncteur intérieur de la 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

Il y a toujours un problème dans cette solution. STL sort appelé le distracteur de l'objet qui lui est passé comme comparateur, cela ruinerait mon programme !!! – Navid

+1

@Navid Regardez ma réponse éditée. –

13

Comme le dit Andreas Brinck, doCompare doit être statique (+1). Si vous devez avoir un état dans votre fonction de comparaison (en utilisant les autres membres de la classe), alors vous feriez mieux d'utiliser un foncteur au lieu d'une fonction (et ce sera plus rapide):

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

L'utilisation d'un foncteur est toujours mieux, juste plus de type (qui peut être unconvenient mais bon ...)

Je pense que vous pouvez également utiliser std :: lier à la fonction de député, mais je ne suis pas sûr de savoir comment et qui ne être facile à lire quand même. MISE À JOUR 2014: Aujourd'hui, nous avons accès aux compilateurs C++ 11, donc vous pourriez utiliser un lambda à la place, le code serait plus court mais aurait exactement la même sémantique.

+0

Enfin, j'ai trouvé une explication raisonnable pour savoir comment faire cela ..! Merci. – porgarmingduod

+0

Très agréable - et cela peut facilement être adapté à une solution générique qui prend une classe ptr + méthode ptr. – tenfour

2

Il existe un moyen de faire ce que vous voulez, mais vous devez utiliser un petit adaptateur.Comme la STL ne pas écrire pour vous, peut peut écrire vous-même:

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

Ensuite, vous pouvez l'utiliser:

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

Vous pouvez utiliser boost::bind:

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

La solution proposée par Rob est maintenant valide C++ 11 (pas besoin de Boost):

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

En effet, comme mentionné par Klaim, lambdas sont une option, un peu plus bavard (vous devez "répéter" que les arguments sont ints):

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

14 C++ prend en charge auto ici:

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

mais encore, vous avez déclaré que les arguments sont passés par copie. Ensuite, la question est "laquelle est la plus efficace". Cette question a été traitée par Travis Gockel: Lambda vs Bind. Son programme de référence donne sur mon ordinateur (OS X Core 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 

lambda est un lambda utilisé directement, et lambda bound est un lambda stocké dans un std::function. Il semble donc que lambdas soit une meilleure option, ce qui n'est pas trop surprenant puisque le compilateur dispose d'informations de niveau supérieur dont il peut tirer profit.

+0

les deuxième et troisième exemples devraient aller sans en utilisant l'espace de noms std :: placeholders; –

+0

@RuslanZasukhin Merci, corrigé. – akim

0

Une manière très simple d'utiliser efficacement une fonction membre consiste à utiliser l'opérateur <. Autrement dit, si vous avez une fonction appelée comparer, vous pouvez l'appeler à partir de l'opérateur <. Voici un exemple de travail:

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

Ensuite, vous ne même pas besoin de donner le nom de la fonction à 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

Mise à jour réponse Graham Asher, comme vous n'avez pas besoin compare mais peut utiliser directement l'opérateur less.

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