Реализация операторов сравнения через «tuple» и «tie» – хорошая идея?

(Примечание: tuple и tie можно взять из Boost или C ++ 11.)
При написании небольших структур, содержащих только два элемента, я иногда предпочитаю выбрать std::pair , так как для этого типа данных уже используется весь важный материал, например operator< для строго-слабого порядка.
Недостатки, хотя и являются довольно бесполезными именами переменных. Даже если бы я сам создал этот typedef , я не буду вспоминать через 2 дня, что first и second точно было, особенно если они оба одного типа. Это становится еще хуже для более чем двух участников, так как pair гнездятся в значительной степени отстойными.
Другим вариантом для этого является tuple , будь то 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 решения я могу оставить некоторых членов, если они не имеют значения для заказа

Есть ли недостатки в этой реализации, которые мне нужно рассмотреть?

Это, безусловно, облегчит запись правильного оператора, чем сканирование его самостоятельно. Я бы сказал, что рассмотрим другой подход, если профилирование показывает, что операция сравнения является трудоемкой частью вашего приложения. В противном случае легкость поддержания этого должна перевешивать любые возможные проблемы с производительностью.

Я столкнулся с этой проблемой, и мое решение использует c ++ 11 variadic templates. Вот код:

Часть .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 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 ); 

По-моему, вы все еще не решаете ту же проблему, что и std::tuple а именно, вам нужно знать как количество, так и имя каждой переменной-члена, вы дублируете ее дважды в функции. Вы можете выбрать private наследование.

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

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

Если вы планируете использовать более одной перегрузки оператора или несколько методов из кортежа, я бы рекомендовал сделать tuple членом classа или получить из кортежа. В противном случае, вы делаете гораздо больше работы. При выборе между ними важно ответить на важный вопрос: хотите ли вы, чтобы ваш class был кортежем? Если нет, я бы рекомендовал содержать кортеж и ограничивать интерфейс, используя делегирование.

Вы можете создать аксессоров для «переименования» членов кортежа.

Давайте будем гением компьютера.