Часто я использую 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; }
};
Это лучше, так как мы можем получить доступ к переменным через достаточно описательное имя. Проблема в том, что вы все еще можете получить доступ к первому и второму, поэтому абстракция легко просочиться. Кроме того, доступ к простым переменным через функции делает синтаксис раздражающим.
Очевидный следующий шаг - сделать наследство частным:
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, у нас теперь нет доступа к перегрузке operator <, поскольку у нас нет доступа к first и second. Это означает, что мы должны определить функцию пересылки для каждой перегрузки оператора, которую мы хотим. Для меня это обычно ==,! = И <, но могут быть и другие, которые я бы хотел. Да, я знаю, что мне, вероятно, не следует перегружать оператор <, чтобы просто вставить его в ассоциативный контейнер, но это делает все чертовски простым ... И определение этих операторов для каждого нового типа является проблемой, и мы ДОЛЖНЫ иметь доступ через функции , Мы можем это исправить: </p>
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
Так что теперь у нас есть простой доступ к переменным, но теперь определение перегруженных операторов является еще более трудной задачей, потому что вместо того, чтобы просто перенаправлять их в реализацию пары, нам на самом деле приходится каждый раз заново их реализовывать ...
Есть ли какие-то решения, которые я упустил, чтобы сделать это легко без недостатков? Если нет, что бы вы предпочли?