2010-12-13 61 views
2

J'ai un ensemble de pointeurs que je voudrais parcourir de manière déterministe. Évidemment, si j'utilise l'ordre de tri par défaut pour set, cela sera basé sur les adresses mémoire des pointeurs qui peuvent être différentes chaque fois que le programme s'exécute. Donc, je définis un comparateur personnalisé que je veux utiliser, mais je ne veux pas changer le type de modèle de l'ensemble (parce qu'il est utilisé dans un million de places dans le code), donc je veux passer dans un objet de comparaison à la constructeur établi qui est dérivé de std :: moinsComparateur personnalisé via un constructeur explicite pour trier std :: set

class TestClass 
{ 
public: 
    TestClass(int id_) : id(id_) {} 
    ~TestClass()     {} 
    int getId() const    { return id;} 
    void setId(int id_)    { id = id_; } 
private: 
    int id; 
}; 

struct TestClassLessThan : public std::less<TestClass*> 
{ // functor for operator< 
    bool operator()(const TestClass* &_Left, const TestClass* &_Right) const 
    { // apply operator< to operands 
     return (_Left->getId() < _Right->getId()); 
    } 
}; 


int main(void) 
{ 
    TestClassLessThan comp; 
    set<TestClass*> testSet(comp), testSet2(comp); 

    TestClass* obj1 = new TestClass(1); 
    TestClass* obj2 = new TestClass(2); 
    testSet.insert(obj1); 
    testSet.insert(obj2); 

    TestClass* obj = *(testSet.begin()); 

    cout << "First run" << endl; 
    BOOST_FOREACH(TestClass* o, testSet) // expecting 1,2 - get 1,2 
     cout << o->getId() << endl; 

    // now change the ordering (based on id) and insert into a new set in the same order 
    obj1->setId(3); 
    testSet2.insert(obj1); 
    testSet2.insert(obj2); 

    cout << "Second run" << endl; 
    BOOST_FOREACH(TestClass* o, testSet2) // expecting 2,3 - get 3,2 
     cout << o->getId() << endl; 

    delete obj1; 
    delete obj2; 
} 

donc ma question est, qu'ai-je oublié de le faire?

Répondre

3

Tout ce qui précède est valide. Une solution possible est de personnaliser std :: moins en utilisant la spécialisation du modèle lui-même:

namespace std 
{ 
    template<> 
    struct less< TestClass*> 
    { // functor for operator< 
    public: 
     bool operator()( TestClass* const &_Left, TestClass* const &_Right) const 
     { // apply operator< to operands  
      return (_Left->getId() < _Right->getId()); 
     } 
    }; 
} 

Vous obtenez alors votre comportement personnalisé à l'aide du comparateur de modèle par défaut pour std :: set. En fonction de la manière dont vous construisez votre système, vous pouvez avoir des problèmes si l'ensemble est "utilisé dans un million d'endroits" et si std :: less personnalisé n'est pas disponible de manière cohérente.

+0

c'est parfait ...mais vous dites que si je déclare définir dans une classe mais il est ensuite passé à un autre qui n'inclut pas la coutume moins - cette deuxième classe peut avoir un comportement différent/indéfini? Je pense que je vais m'assurer que la définition de l'ensemble et ses méthodes d'accesseur incluent toutes les personnalisations moins spécialisées. –

3

Le type d'argument formel du constructeur std::set pour l'objet comparateur est Compare const&, où Compare est le paramètre modèle. Par conséquent, même si l'objet set conservait une référence à votre objet comparateur actuel (au lieu de le copier), il serait traité comme le type Compare, que vous avez par défaut à std::less.

Et puisque std::less est qu'il est non polymorphes std::less::operator() qui est appelé, pas votre operator() dans TestClassLessThan.

Donc, en un mot, "vous ne pouvez pas faire ça". Ou plutôt, vous pouvez, comme votre code l'illustre, mais vous n'obtenez aucun changement de comportement.

Pour modifier l'objet de comparaison, vous devez spécifier un autre type Compare comme argument de modèle.

Ce qui est ce que vous vouliez éviter, mais désolé, il n'y a aucun moyen de contourner (que je connais).

Vive & HTH.,

1

Ce n'est pas la façon d'utiliser set d'un comparateur. operator() de std :: less n'est pas une fonction virtuelle et ne sera pas surchargée. Au lieu de cela, utilisez cette méthode pour initialiser et cela fera l'affaire. Pour que cela fonctionne, votre fonction de comparaison doit accepter les pointeurs const et non les pointeurs vers const. Pour que cela fonctionne, votre fonction de comparaison doit accepter les pointeurs const. Pour être sûr, vous pouvez le changer à

// functor for operator< 
    bool operator()(const TestClass* const&_Left, const TestClass* const&_Right) const 
    { 
     cout << "comparing"; 
     // apply operator< to operands 
     return (_Left->getId() < _Right->getId()); 
    } 
+1

Si vous lisez la question, vous remarquerez que je ne veux pas changer la signature de l'ensemble. –