Часто мне приходится использовать std:: pair для определения логических группировок двух связанных величин как аргументов функции/возвращаемых значений. Некоторые примеры: строка/столбец, тег/значение и т.д.
Часто мне нужно катавать собственный класс, а не просто использовать std:: pair. Это довольно легко увидеть, когда все начинает разрушаться - когда код становится заваленным make_pair, во-первых, и во-вторых, его очень сложно запомнить, что есть что - std::pair<int, int>
передает меньше значения, чем тип Position
.
Что вы нашли, это лучшие способы обернуть функциональность std:: pair в тип, который передает реальный смысл?
Вот некоторые вещи, которые я рассмотрел:
typedef std::pair<int, int> Position;
Это, по крайней мере, дает тип значимому имени при его передаче, но тип не применяется, его по-прежнему на самом деле просто пара, и большинство из тех же проблем все еще существует. Это очень просто написать.
struct Position : public std::pair<int, int>
{
typedef std::pair<int, int> Base;
Position() : Base() {}
Position(const Position &x) : Base(x) {}
Position(int a, int b) : Base(a, b) {}
int &row() { return first; }
const int &row() const { return first; }
int &col() { return second; }
const int &col() const { return second; }
};
Это лучше, так как мы можем получить доступ к переменным с помощью достаточно описательного имени. Проблема здесь в том, что вы все еще можете получить доступ первым и вторым, поэтому легко абстрагироваться от утечки. Кроме того, доступ к простым переменным через функции делает синтаксис раздражающим.
Очевидным следующим шагом является наследование private:
struct Position : private std::pair<int, int>
{
typedef std::pair<int, int> Base;
Position() {}
Position(const Position &x) : Base(x) {}
Position(int a, int b) : Base(a, b) {}
int &row() { return first; }
const int &row() const { return first; }
int &col() { return second; }
const int &col() const { return second; }
bool operator<(const Position &x) const { return Base(*this) < Base(x); }
// other forwarding operators as needed...
};
Итак, теперь, по крайней мере, мы избавились от доступа к первому и второму, но теперь появляется новая проблема. Когда мы хотим сохранить тип в std:: set, теперь у нас нет доступа к оператору < перегрузка, поскольку у нас нет доступа к первой и второй. Это означает, что мы должны определить функцию пересылки для каждой требуемой перегрузки оператора. Для меня это обычно ==,!= И <, но могут быть другие, которые я бы хотел. Да, я знаю, что я, вероятно, не должен перегружать оператора < просто придерживаться его в ассоциативном контейнере, но он делает все настолько простыми... И определение этих операторов для каждого нового типа является болью, и мы STILL должны получить доступ через функции. Мы можем исправить это:
struct Position
{
Position() {}
Position(const Position &x) : row(x.row), col(x.col) {}
Position(int row, int col) : row(row), col(col) {}
int row, col;
};
bool operator<(const Position &a, const Position &b)
{
return a.row < b.row || (!(b.row < a.row) && a.col < b.col);
}
// more overloads as needed
Итак, теперь у нас есть простой доступ к переменной, но теперь определение перегруженных операторов еще больнее, потому что вместо того, чтобы просто перенаправлять их на реализацию пары, мы должны каждый раз их повторно выполнять...
Есть ли какие-либо решения, которые я упускал из виду, сделать это легко без недостатков? Если вы не хотите, чтобы вы предпочитали?