Впровадження операторів порівняння за допомогою “кортеж” та “краватка”, хороша ідея?


98

(Примітка: tupleі tieможе бути взято з Boost , або C ++ 11.)
При написанні невеликих структур тільки з двох елементів, я іноді , як правило, вибрати std::pair, так як всі важливі речі вже зроблена для цього типу даних, як operator<для суворого слабкого упорядкування .
Недоліками є, однак, досить марні імена змінних. Навіть якщо я сам це створив typedef, я не пам’ятатиму через 2 дні, що firstі що secondсаме було, особливо якщо вони обидва однотипні. Це стає ще гіршим для більш ніж двох членів, оскільки вкладеність pairмайже відмовна.
Інший варіант - atuple, або від Boost, або від C ++ 11, але це насправді не виглядає нічим приємнішим та зрозумілішим. Тому я повертаюся до написання конструкцій самостійно, включаючи всі необхідні оператори порівняння.
Оскільки особливо це operator<може бути досить громіздким, я думав обійти весь цей безлад, просто покладаючись на операції, визначені для tuple:

Приклад operator<, наприклад, для строгого та слабкого впорядкування:

bool operator<(MyStruct const& lhs, MyStruct const& rhs){
  return std::tie(lhs.one_member, lhs.another, lhs.yet_more) <
         std::tie(rhs.one_member, rhs.another, rhs.yet_more);
}

( tieРобить tupleз T&посилань з переданих аргументів.)


Редагувати : пропозиція від @DeadMG приватно успадковувати від tupleне є поганою, але вона має цілий ряд недоліків:

  • Якщо оператори стоять окремо (можливо, друзі), мені потрібно успадкувати публічно
  • За допомогою кастингу мої функції / оператори ( operator=зокрема) можна легко обійти
  • За допомогою tieрішення я можу виключити певних учасників, якщо вони не мають значення для замовлення

Чи є у цій реалізації недоліки, які мені потрібно врахувати?


1
Мені
здається

1
Це дуже розумна ідея, навіть якщо вона не спрацьовує. Мені доведеться розслідувати це.
templatetypedef

Це виглядає цілком розумно. Єдина помилка, про яку я зараз можу подумати, - це те, що tieне може бути застосована до членів бітового поля.
Ісе Вістерія,

4
Мені ця ідея подобається! Якщо tie(...)виклики будуть дублюватися в різних операторах (=, ==, <і т. Д.), Ви можете написати приватний вбудований метод, make_tuple(...)щоб інкапсулювати це, а потім викликати його з різних інших місць, як у return lhs.make_tuple() < rhs.make_tuple();(хоча тип повернення з цей метод може бути цікаво оголосити!)
aldo

13
@aldo: C ++ 14 на допомогу! auto tied() const{ return std::tie(the, members, here); }
Xeo

Відповіді:


60

Це, безумовно, полегшить написання правильного оператора, ніж його прокручування самостійно. Я б сказав, що розглядайте інший підхід лише в тому випадку, якщо профілювання показує, що операція порівняння є трудомісткою частиною вашої програми. В іншому випадку простота в підтримці цього повинна перевищувати будь-які можливі проблеми з продуктивністю.


17
Я не можу уявити собі випадок, коли tuple<>'' operator<буде щось повільніше, ніж рукописне.
ildjarn

51
Одного разу я прийшов до цієї точно такої ж ідеї та зробив кілька експериментів. Був позитивно здивований, побачивши, що компілятор виклав та оптимізував все, що пов'язано з кортежами та посиланнями, видаючи збірку майже ідентичну рукописному коду.
JohannesD

7
@JohannesD: Я можу підтримати це свідчення, зробив те саме один раз
sehe

Чи гарантує це суворе слабке впорядкування ? Як?
CinCout,

5

Я зіткнувся з цією ж проблемою, і моє рішення використовує варіативні шаблони c ++ 11. Ось код:

Частина .h:

/***
 * Generic lexicographical less than comparator written with variadic templates
 * Usage:
 *   pass a list of arguments with the same type pair-wise, for intance
 *   lexiLessthan(3, 4, true, false, "hello", "world");
 */
bool lexiLessthan();

template<typename T, typename... Args>
bool lexiLessthan(const T &first, const T &second, Args... rest)
{
  if (first != second)
  {
    return first < second;
  }
  else
  {
    return lexiLessthan(rest...);
  }
}

І .cpp для базового випадку без аргументів:

bool lexiLessthan()
{
  return false;
}

Тепер вашим прикладом стає:

return lexiLessthan(
    lhs.one_member, rhs.one_member, 
    lhs.another, rhs.another, 
    lhs.yet_more, rhs.yet_more
);

Я помістив подібне рішення тут, але не вимагаючи оператора! =. stackoverflow.com/questions/11312448 / ...
steviekm3

3

На мій погляд, ви все ще не std::tupleвирішуєте ту ж проблему, що і розв'язує - а саме, ви повинні знати і кількість, і ім'я кожної змінної-члена, ви дублюєте це двічі у функції. Ви можете вибрати privateспадщину.

struct somestruct : private std::tuple<...> {
    T& GetSomeVariable() { ... }
    // etc
};

Такий підхід є трохи трохи більше безладу , щоб почати с, але ви підтримуєте тільки змінні і імена в одному місці, а НЕ в будь-якому місці для будь-якого оператора , який ви хочете перевантаження.


3
Отже, я б використовував іменовані засоби доступу до змінних типу T& one_member(){ return std::get<0>(*this); }etc? Але чи не потрібно мені це надавати такий метод для кожного "члена", який я маю, включаючи перевантаження для версії const та non-const?
Xeo

@Xeo Я не бачу, що іменовані засоби доступу вимагають більше роботи, ніж створення фактичних змінних. У будь-якому випадку вам потрібно мати окреме ім'я для кожної змінної. Я припускаю, що буде дублювання для const / non-const. Тим не менш, ви можете створити шаблон усієї цієї роботи.
Lee Louviere

1

Якщо ви плануєте використовувати більше одного перевантаження оператора або більше методів з кортежу, я рекомендую зробити кортеж членом класу або вивести з кортежу. В іншому випадку те, що ви робите, - це набагато більше роботи. Вирішуючи питання між цими двома, важливим питанням, на яке потрібно відповісти, є: чи хочете ви бути в класі кортеж? Якщо ні, я б рекомендував містити кортеж та обмежувати інтерфейс за допомогою делегування.

Ви можете створити засоби доступу, щоб "перейменувати" членів кортежу.


Я прочитав запитання ОП у значенні "чи реалізую свій клас" operator<за допомогою std::tieрозумного? " Я не розумію, як ця відповідь стосується цього питання.
ildjarn

@ildjarn Є кілька коментарів, які я тут не розміщував. Я все зібрав, щоб читалося краще.
Lee Louviere
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.