Пользовательский компаратор через явный конструктор для сортировки std :: set - PullRequest
2 голосов
/ 13 декабря 2010

У меня есть набор указателей, которые я хотел бы перебрать детерминированным образом. Очевидно, что если я использую порядок сортировки по умолчанию для set, он будет основан на адресах памяти указателей, которые могут отличаться при каждом запуске программы. Поэтому я определяю собственный компаратор, который хочу использовать, но я не хочу менять шаблонный тип набора (поскольку он используется в миллионе мест в коде), поэтому я хочу передать объект компаратора конструктор множества, производный от std :: less

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

Итак, мой вопрос: что я забыл сделать?

Ответы [ 3 ]

3 голосов
/ 13 декабря 2010

Все вышеперечисленное действует.Возможное решение состоит в том, чтобы настроить саму std :: less, используя специализацию шаблона:

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

Затем вы можете настроить свое поведение, используя компаратор шаблона по умолчанию для std :: set.

В зависимости отвы строите свою систему, у вас могут возникнуть проблемы, если набор «используется на миллион мест» и пользовательский std :: less не всегда доступен.

3 голосов
/ 13 декабря 2010

Тип формального аргумента конструктора std::set для объекта сравнения - Compare const&, где Compare - параметр шаблона.

Таким образом, даже если объект set сохранил ссылку на ваш фактический объект компаратора (вместо того, чтобы копировать его), он будет обрабатываться как тип Compare, который по умолчанию установлен на std::less.

А поскольку std::less не является полиморфным, то вызывается std::less::operator(), а не ваш operator() в TestClassLessThan.

Так что, вкратце, «вы не можете этого сделать».

Вернее, вы можете, как показывает ваш код, но вы не получаете никаких изменений поведения.

Чтобы изменить объект сравнения, вы должны указать другой тип Compare в качестве аргумента шаблона.

Чего вы хотели избежать, но извините, пути нет (о котором я знаю).

Приветствия и hth.,

1 голос
/ 13 декабря 2010

Это не способ использовать набор с компаратором.operator () из std :: less не является виртуальной функцией и не будет переопределен.

Вместо этого используйте этот способ для инициализации, и он сделает свое дело.работа, ваша функция сравнения должна принимать указатели const, а не указатели на const.Чтобы быть в безопасности, вы можете изменить его на

 // 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());
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...