Пользовательский компаратор через явный конструктор для сортировки std::set

У меня есть набор указателей, которые я хотел бы перебрать детерминированным образом. Очевидно, что если я использую порядок сортировки по умолчанию для 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 ответа

Решение

Все вышеперечисленное действует. Возможное решение состоит в том, чтобы настроить 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 не всегда доступен.

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

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

И с тех пор std::less это не полиморфный это std::less::operator() это называется, а не ваш operator() вниз в TestClassLessThan,

Итак, если коротко, то "ты не можешь этого сделать".

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

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

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

Ура & hth.,

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

Вместо этого используйте этот способ для инициализации, и он сделает свое дело.

set<TestClass*, TestClassLessThan> testSet, testSet2;

Чтобы это работало, ваша функция сравнения должна принимать указатели 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());
    }
Другие вопросы по тегам