Реализация операторов сравнения через 'кортеж ' и' галстук', хорошая идея?
(Примечание: tuple
и tie
можно взять из Boost или C++11.)
При написании небольших структур только с двумя элементами, я иногда склонен выбирать std::pair
, поскольку все важные вещи уже сделаны для этого типа данных, например operator<
для строгого слабого заказа.
Недостатками, однако, являются довольно бесполезные имена переменных. Даже если я сам создал это typedef
, я не буду помнить 2 дня спустя, что first
и что second
точно, особенно если они оба одного типа. Это становится еще хуже для более, чем двух членов, как гнездовье pair
s в значительной степени отстой.
Другой вариант для этого является 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
of T&
ссылки из переданных аргументов.)
Edit: предложение от @DeadMG в частном порядке наследовать от tuple
это не плохо, но он получил довольно некоторые недостатки:
- если операторы свободны (возможно, друзья), мне нужно наследовать публично
- С кастинг, мои функции / операторы (
operator=
в частности) можно легко обойти - С
tie
решение, я могу оставить некоторые члены, если они не имеют значения для заказа
есть ли какие-либо недостатки в этой реализации, которые мне нужно рассмотреть?
4 ответа:
Это, безусловно, сделает его легче написать правильный оператор, чем прокатки его самостоятельно. Я бы сказал, что только рассмотрите другой подход, если профилирование показывает, что операция сравнения является трудоемкой частью вашего приложения. В противном случае простота поддержания этого должна перевешивать любые возможные проблемы с производительностью.
Я пришел через эту же проблему и мое решение использует C++11 вариативные шаблоны. Вот код:
The .ч. ч.:
/*** * 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 );
на мой взгляд, вы все еще не решаете ту же проблему, что и
std::tuple
решает-а именно, вы должны знать, сколько и имя каждой переменной-члена, вы дублируете его дважды в функции. Вы можете выбратьprivate
наследование.struct somestruct : private std::tuple<...> { T& GetSomeVariable() { ... } // etc };
Это мало немного больше беспорядка для начала, но вы только поддерживаете переменные и имена в одном месте, а не в каждом месте для каждого оператора, который вы хотите перегрузить.
Если вы планируете использовать более чем одну перегрузку оператора или несколько методов из кортежа, я бы рекомендовал сделать кортеж членом класса или производным от кортежа. В противном случае, то, что вы делаете намного больше работы. При выборе между ними, важный вопрос, чтобы ответить: вы хотите, чтобы ваш класс быть кортеж? Если нет, я бы рекомендовал содержать кортеж и ограничивать интерфейс с помощью делегирования.
вы можете создать методы доступа для "переименования" членов кортеж.