Как вы пишете оператор () или меньше, чем функтор аккуратнее, чем функция сравнения-сравнения - PullRequest
8 голосов
/ 05 октября 2010

Написание оператора <() для структуры кажется более понятным, чем написание классического сравнения с тремя значениями.</p>

например, для сортировки следующих

struct S {
    int val;
};

вы можете написать оператор <</strong> ()

bool operator< ( const S &l, const S &r ) {
     return l.val < r.val;
}

или a trivalue function ( обычно следующим образом )

int compare( const S &l, const S &r ) {
    if( r.val > l.val ) return 1;
    if( r.val < l.val ) return -1;
    return 0;
}

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

Но эта мысль немного обманчива в более сложных структурах:

struct S {
    int x;
    int y;
};

следующее ясно, и начинающие склоннынапишите это так

bool operator< ( const S &l, const S &r ) {
     if( l.x < r.x ) return true;
     if( l.y < r.y ) return true;
     return false;
}

но это неправильно !Вы не можете правильно сортировать это!

И нужно некоторое время, чтобы подумать, что вам действительно нужно написать это так, чтобы

bool operator< ( const S &l, const S &r ) {
     if( l.x < r.x ) return true;
     if( l.x > r.x ) return false;
     if( l.y < r.y ) return true;
     if( l.y > r.y ) return false;
     return false;
}

работало правильно.

Можете ли вы, и вы написать такую ​​функцию сравнения лучше или яснее? Старая функция сравнения трех значений по крайней мере "заставила" задуматься о>, <и == случаи.</p>

Ответы [ 7 ]

5 голосов
/ 05 октября 2010

Если меня не волнует производительность или компилятор, я склонен использовать это:

return make_tuple(l.x, l.y, ...) < make_tuple(r.x, r.y, ...);

И для чуть менее дорогой в плане копий версии:

return tie(cref(l.x), cref(l.y), ...) < tie(cref(r.x), cref(r.y), ...);

Кстати, вторая версия также работает с lvalues.

4 голосов
/ 05 октября 2010

В случае int вы можете просто написать:

return l.x < r.x || (l.x == r.x && l.y < r.y);

Только вы говорите о типе, который не имеет == с правильным поведением, вам нужно использовать что-то более сложное, даже если это не так уж плохо.

return l.x < r.x || (!(r.x < l.x) && l.y < r.y);

Распространение на большее количество членов:

return l.x < r.x ||
      !(r.x < l.x) && (l.y < r.y ||
      !(r.y < l.y) && (l.z < r.z ||
      /* ... */
      ) /* lisp-like sequence of ) */ );

Если вы можете расположить своих членов в массиве или другом контейнере, вы можете использовать std::lexicographical_compare.

4 голосов
/ 05 октября 2010

Мне нравится делать это следующим образом:

bool operator< ( const S &l, const S &r )
{
    if( l.x != r.x ) return l.x < r.x;
    else return l.y < r.y;
}

РЕДАКТИРОВАТЬ: обратите внимание, что это также одна полезная функция std::pair - она ​​уже определяет это, поэтому вы не можете ошибиться.

3 голосов
/ 05 октября 2010

Дело в том, что вы можете просто объявить одну функцию сравнения трех значений, если вы автоматически генерируете все операторы, используя: http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick

1 голос
/ 05 октября 2010

Это не яснее и не короче, чем ваш последний пример, но оно имеет то преимущество, что не требует от членов ничего, кроме operator<.

bool operator< ( const S &l, const S &r ) { 
     if( l.x < r.x ) return true; 
     if( r.x < l.x ) return false; 
     if( l.y < r.y ) return true; 
     if( r.y < l.y ) return false; 
     return false; 
} 

Последний случай всегда можно упростить, к сожалению, предыдущие случаи всегда должны быть более длинной формы.

bool operator< ( const S &l, const S &r ) { 
     if( l.x < r.x ) return true; 
     if( r.x < l.x ) return false; 
     return  l.y < r.y; 
} 
0 голосов
/ 05 октября 2010

Для первой трехзначной compare() функции

int compare( const S &l, const S &r ) {
    return r.val - l.val;
}

Для более поздней

bool operator< ( const S &l, const S &r ) {
     return (l.x < r.x) || ((l.x == r.x) && (l.y < r.y));
}
0 голосов
/ 05 октября 2010

Одна вещь, которую я однажды сделал, которая казалась полезной идиомой, заключалась в написании функции сравнения, которая возвращает логическое значение, которое принимает два аргумента плюс логическое значение, называемое «смещение».Функция возвращает true, если первый аргумент больше, или если установлено смещение, и два аргумента равны.Таким образом, сравнение, результат которого зависел от двух субсравнений, было бы:

  if (compare(a.part1,b.part1,compare(a.part2,b.part2,bias)))
    ...

Обратите внимание, что это сделает «дополнительные» сравнения, так как части 2 будут сравниваться, даже если сравнение части 1 будет достаточным для полученияответьте, но если избегаете лишних тестов на равенство.

В противном случае, используя трехзначную логику, можно сделать что-то вроде:

  int result;

  if ((result = compare(a.part1,b.part1)) != 0) return result;
  if ((result = compare(a.part2,b.part2)) != 0) return result;

Последняя форма избегает ненужных сравнений и легко расширяемана любое количество полей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...