我经常发现自己使用 std::pair 将两个相关量的逻辑分组定义为函数参数/返回值。一些示例:行/列、标签/值等。
很多时候我真的应该滚动我自己的类,而不是仅仅使用 std::pair。当事情开始崩溃时,很容易看出 - 当代码中充斥着 make_pair、first 和 secondary 时,很难记住什么是什么 -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
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
所以现在我们有了简单的变量访问,但现在定义重载运算符更加痛苦,因为我们不仅将它们转发到该对的实现,而且实际上每次都必须重新实现它们......
有没有我忽略的解决方案可以让这件事变得简单而又没有缺点?如果没有,您会更喜欢哪个?