Что _can_ я использую в качестве ключей std :: map?

Расширяется.

У меня есть:

struct Coord
{
  int row, col ;

  bool operator<( const Coord& other ) const
  {
    return row < other.row && col < other.col ;
  }
} ;

Я пытаюсь создать map<Coord, Node*>, где вы можете найти Node* по Coord.

Проблема в том, что в нем есть ошибки. Поиск в map<Coord, Node*> от Coord возвращает неправильные.

Мне трудно понять, уместно это или нет.

Википедия говорит: map [keys] требует строгого слабого упорядочивания . Я сделал это неправильно? Есть ли способ заставить его работать, или ключи для карты должны быть простыми значениями, которые можно «строго упорядочить»?

В основном вопрос в том, что требуется для того, чтобы пользовательский struct работал как ключ для моей std :: map?


person bobobobo    schedule 06.12.2009    source источник
comment
Не могли бы вы подробнее рассказать об ошибках? Ваш код компилируется? Вернуть неправильные результаты? Крушение?   -  person    schedule 07.12.2009
comment
std :: map не содержит ошибок. По крайней мере, в MS STL, который я видел. Также Википедия - не лучший источник RTFN.   -  person Captain Comic    schedule 07.12.2009
comment
Под словом «имеет ошибки» я подразумеваю следующее предложение: Поиск на карте ‹Coord, Node *› от Coord возвращает неправильные.   -  person bobobobo    schedule 07.12.2009


Ответы (5)


Да, у вас вполне могут возникнуть проблемы со строгим-слабым порядком. Скорее всего, он не работает, как вы ожидали. Учитывать:

  bool operator<( const Coord& other ) const
  {
    return row < other.row && col < other.col ;
  }

obj1 (эта) строка: 2 столбец: 3

obj2 строка: 3 столбец: 2

obj1 ‹obj2? => ложь

хорошо, тогда:

obj2 ‹obj1? => ложь

Единственный вывод - они должны быть равны (на основе вашего оператора ‹). Поскольку это карта, а ключи уникальны, оба ключа возвращаются в одно и то же место. Такое поведение может быть, а может и не быть тем, чего вы ожидаете, но похоже, что это не так.

Что вам нужно, так это сделать приоритет между строкой / столбцом, чтобы ‹действительно работал так, как вы ожидаете:

  bool operator<( const Coord& other ) const
  {
     // look at row first, if row is equal, check column.
     if (row < other.row)
     {
         return true;
     }
     else if (row == other.row)
     {
         return col < other.col ;
     }
     return false;
  }
person Doug T.    schedule 06.12.2009
comment
или иначе return row < other.row || (row == other.row && col < other.col) - person David Rodríguez - dribeas; 07.12.2009
comment
Проблема заключается не только в том, что сравнение может не выполнить то, что от него требуется, но и в том, что эквивалентность согласно исходному определению не является отношением эквивалентности, и это нарушает требования для использования в качестве ключа в std::map. - person CB Bailey; 07.12.2009
comment
+1, поэтому профессионалы используют tie(col, row) < tie(other.col, other.row);) - person avakar; 07.12.2009

Вы, наверное, захотите:

 bool operator<( const Coord& other ) const
  {
    if ( row < other.row ) {
       return true;
    }
    else if ( row == other.row ) {
       return col < other.col;
    }
    else {
       return false ;
    }
  }

или наоборот. Этот меня тоже несколько раз укусил!

person Community    schedule 06.12.2009
comment
Спасибо за принятие, но я подумал, что ответ Дуга Т был намного лучше и включает в себя то, что я написал. - person ; 07.12.2009
comment
Что ж, ваш ответ на вопрос, и был раньше. Но я это изменю. - person bobobobo; 07.12.2009

попробуй это:

struct Coord
{
  int row, col ;

  bool operator<( const Coord& other ) const
  {
    if (row != other.row)
      return row < other.row;

    return col < other.col ;
  }
} ;
person dcp    schedule 06.12.2009

Чтобы функция сравнения налагала строгий слабый порядок на набор значений для вашего объекта, одно из условий состоит в том, что эквивалентность должна быть транзитивной. a и b считаются эквивалентными, если (в синтаксисе C ++) !(a < b) && !(b < a) истинно.

Ваш operator< не соответствует этому требованию. Рассмотрим a = {1, 3}, b = {3, 2}, c = {2, 1}. В этом случае ни a ‹b, b‹ a не истинны, ни a ‹c, c‹ a не верны. Это означает, что a и b эквивалентны, а a и c эквивалентны, однако ясно, что c ‹b, поэтому b и c не эквивалентны, тем самым показывая, что эквивалентность не транзитивна. Вот почему ваш оператор ‹не подходит для использования Coord в качестве ключа в std::map.

person CB Bailey    schedule 06.12.2009

person    schedule
comment
Правильнее не использовать оператор ==. Чтобы сравнить два элемента с operator<, им нужно только определить operator<. - person GManNickG; 07.12.2009
comment
@GMan Здесь мы говорим о сравнении целых чисел. - person ; 07.12.2009
comment
Как насчет скобок, чтобы прояснить, что && имеет более высокий приоритет, чем ||? - person ; 07.12.2009
comment
@Neil Говоря абстрактно, это очень хорошая мысль. Вы не хотите, чтобы ваш код ломался из-за изменения базовых типов. - person ; 07.12.2009
comment
@super Означает ли это, что вы никогда не используете оператор ==? Я как-то в этом сомневаюсь. - person ; 07.12.2009
comment
Нет, это не так, но я пытаюсь минимизировать требования к базовым типам. Если вы используете здесь только ‹, то базовые типы должны определять только оператор‹, а не одновременно operator ‹и operator ==. Кроме того, хотя здесь может показаться неестественным избегать ==, кажется удовлетворительным, что оператор ‹определяется только в терминах оператора‹ сравниваемых типов. - person ; 07.12.2009
comment
Я бы также выступил за использование только ‹, потому что это дает вам каноническую форму. Например. if (row < other.row) return true; if (other.row < row) return false; return col < other.col; - person MSalters; 07.12.2009