Проблема вставки двойной карты - PullRequest
1 голос
/ 07 сентября 2011

У меня есть stl :: map, ключ которого определен как определенный мной объект, и int.Использование карты заключается в следующем: у меня есть список конкретного объекта, и я хочу подсчитать, сколько у меня идентичных объектов.Поэтому я вставляю объекты в карту. Если объект уже существует на карте, я увеличиваю его значение (отсюда и счетчик).В объекте определены все основные операторы.Объект состоит из 5 строк.Оператор == определен как сравнение всех 5 строк и логически имеет смысл в контексте.Проблема в том, что оператор <не имеет логического значения в контексте.Я забочусь, только если объекты равны.Я не могу определить, какой из двух разных объектов больше. Поэтому ради stl карта определила этот оператор как результат если еще лестница, и в каждом, если я сравнил с «<» еще одну строку из пяти.Если true, верните true иначе, если .... И последнее возвращает false.В конкретном случае объекта, где у меня было три идентичных экземпляра, я получил карту, содержащую два идентичных объекта в качестве ключей, один из них имел счетчик 1, а другой - 2. Я не могу понять, в чем проблема, икак это могло случитьсяДля тех, кто запросил некоторые примеры кода - по причине, которую я не могу объяснить - я не могу опубликовать сам код, но я напишу хороший пример этого (пожалуйста, не обращайте внимания на такие мелочи, как отсутствие ';' - я написал это в 5минут): </p>

class Example
{
private:
    string one;
    string two;
    string three;
    string four;
    string five;
public:
    inline Example (string a_one,string a_two, string a_four, string a_five) :
        one(a_one),two(a_two),three(a_three),four(a_four),five(a_five)
        {}

    inline bool operator == (const Example& other) const
    {
        if (one == other.one)
        {
            if (two == other.two)
            {
                if (three == other.three)
                {
                    if (four == other.four)
                    {
                        if (five == other.five)
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    inline bool operator < (const Example& other) const
    {
        if (one < other.one)
        {
            return true;
        }
        else if (two < other.two)
        {
            return true;
        }
        else if (three < other.three)
        {
            return true ;
        }
        else if (four < other.four)
        {
            return true;
        }
        else if (five < other.five)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

void CountExample(Example& example,std::map<Example,int>& counters);

void main()
{
    std::map<Example,int> counters;
    std::list<Example> examples = GetExamples();
    //GetExamples defined elsewhere, and initializes examples with a long list of instances of Example
    std::list<Example>::const_iterator Iter;
    for (Iter = examples.begin();Iter != examples.end();Iter++)
    {
        CountExample(*Iter);
    }
    PrintCounters(counters);//PrintCounters defined elsewhere and prints the map to a file
}

void CountExample(Example& example,std::map<Example,int>& counters)
{
    std::map<Example,int>::const_iterator Iter;
    Iter = counters.find(example);
    if (Iter ==counters.end()) //means the specific Example is not in the map
    {
        counters.insert(std::pair<Example,int>(example,1));
    }
    else
    {
        counters[example] += 1;
    {
}

Ответы [ 4 ]

2 голосов
/ 08 сентября 2011

Если у вас достаточно современный компилятор, эту лестницу сравнений можно заменить одним сравнением между двумя std::tie() 'd кортежами:

#include <tuple>
...
bool operator== (const Example& other) const
{
    return std::tie(one, two, three, four, five)
        == std::tie(other.one, other.two, other.three, other.four, other.five);
}
bool operator < (const Example& other) const
{
    return std::tie(one, two, three, four, five)
         < std::tie(other.one, other.two, other.three, other.four, other.five);
}

Кстати, может быть проще использовать std::multiset для подсчета количества раз, когда конкретный элемент хранится в ассоциативном контейнере, что упрощает CountExample до однострочного

void CountExample(const Example& example, std::multiset<Example>& counters)
{
    counters.insert(example);
}

Хотя печать становится немного сложнее:

void PrintCounters(const std::multiset<Example>& counters)
{
    for(auto i=counters.begin(); i!=counters.end(); i = counters.upper_bound(*i))
            std::cout << *i << ":" << counters.count(*i) << '\n';
}

Испытание на идеоне: http://ideone.com/uA7ao

1 голос
/ 07 сентября 2011

Для сравнения нескольких элементов каждый сравниваемый элемент будет иметь три результата: меньше, больше или эквивалентно.Вы должны учитывать все эти случаи.

bool LessThan(const MyClass & left, const MyClass right)
{
    if (left.one < right.one)
        return true;
    else if (right.one < left.one)
        return false;
    // equivalent in one
    if (left.two < right.two)
        return true;
    else if (right.two < left.two)
        return false;
    // equivalent in one and two
        ...
    return false;
}
0 голосов
/ 08 сентября 2011

Вы должны предоставить operator< для вашего типа.Это может быть довольно утомительно писать, но вы можете просто сделать это, используя Boost.Tuple - таким образом, кортеж обрабатывает сравнения, делая ваш код легче для чтения, записи и понимания.

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <string>

struct Object
{
    std::string a;
    std::string b;
    std::string c;
};

bool operator<(const Object& obj1, const Object& obj2)
{
    return (boost::tie(obj1.a, obj1.b, obj1.c) < 
        boost::tie(obj2.a, obj2.b, obj2.c));
}
0 голосов
/ 07 сентября 2011

Редактировать: Подумав еще раз о проблеме, я решил удалить свой старый ответ, поскольку он, похоже, не относился к текущей проблеме. Ваш operator< метод, похоже, удовлетворяет требованиям для строгого слабого упорядочения, поэтому я думаю, что проблема кроется где-то еще, и поэтому я оставляю следующее альтернативное решение ниже ...

Похоже, у вас проблемы с созданием общего порядка для вашей карты, поэтому вы можете рассмотреть std::unordered_map как альтернативу, которая будет напрямую применять ваш operator== для определения равенства, а не использовать operator< для строгого слабого порядка ... вам нужно будет предоставить хеш-функцию для вашего класса, но в противном случае использование контейнера std::unordered_map на основе хеш-таблицы будет довольно простым.

...