我经常发现自己使用 std::pair 将两个相关数量的逻辑分组定义为函数参数/返回值。一些示例:行/列、标签/值等。
很多时候,我真的应该滚动自己的课程,而不是仅仅使用 std::pair。当事情开始崩溃时很容易看出——当代码中到处都是 make_pair 时,首先,其次,很难记住什么是什么——与std::pair<int, int>
type 相比,an 传达的意义更少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; }
};
这更好,因为我们可以通过一个合理的描述性名称来访问变量。这里的问题是您仍然可以访问 first 和 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...
};
所以现在至少我们已经摆脱了对first和second的访问,但是现在又出现了一个新问题。当我们想将类型存储在 std::set 中时,我们现在无法访问 operator< 重载,因为我们无法访问 first 和 second。这意味着我们必须为我们想要的每个运算符重载定义一个转发函数。对我来说,这通常是 ==、!= 和 <,但可能还有其他我想要的。是的,我知道我可能不应该重载 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
所以现在我们有了简单的变量访问,但是现在定义重载的操作符更加痛苦,因为不是仅仅将它们转发到对的实现,我们实际上每次都必须重新实现它们......
有没有我忽略的解决方案可以让这件事变得简单而没有缺点?如果没有,你会倾向于哪个?