我经常发现自己使用std :: pair来定义两个相关量的逻辑分组作为函数参数/返回值。一些例子:row / col,tag / value等。
我常常应该滚动自己的类而不是仅仅使用std :: 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中时,我们现在无法访问运算符&lt;超载,因为我们无法访问第一个和第二个。这意味着我们必须为每个我们想要的运算符重载定义转发函数。对我来说,这通常是==,!=和&lt;,但可能还有其他我想要的。是的我知道我可能不应该重载运算符&lt;只是将它粘贴在一个关联容器中,但它使一切变得如此简单......为每种新类型定义这些运算符是一种痛苦,我们仍然必须通过函数访问。我们可以解决这个问题:
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
所以现在我们有了简单的变量访问,但是现在定义重载的运算符更加困难,因为我们实际上每次都必须重新实现它们,而不是仅将它们转发给它们...
是否有任何我忽略的解决方案,使这很容易没有缺点?如果没有你更喜欢哪一个?
答案 0 :(得分:6)
这就是Boost.Tuple的目的。
但你现在可能应该使用std::tuple ......
答案 1 :(得分:4)
一位同事向我指出了两种可能的解决方案:
使用boost strong typedef作为typedef的改进版本。我以前从来没有听说过这个,它似乎并不真正属于任何子库,只是浮动。
使用宏来生成不同运算符所需的代码。这样我就不必在每个定义级别上明确地写任何东西,只需执行类似DEFINE_PAIR_TYPE(Position, int, int, row, col);
的操作。这可能与我正在寻找的最接近,但与其他人提出的一些解决方案相比,它仍然有点邪恶。
答案 2 :(得分:3)
还有Boost::Operators库可自动生成操作员代码。它类似于Martin York suggested的SGI库,但可能更便携。
答案 3 :(得分:2)
您仍然可以通过转发来重复使用pair
功能:
bool operator< ( const Position &a, const Position &b )
{
return
std::make_pair( a.row, a.col ) < std::make_pair( b.row, b.col );
}
虽然你最终仍然需要为你需要的每一个操作都这样做......
答案 4 :(得分:2)
您可以使用一些标准实用程序模板来帮助定义关系运算符。
#include&lt; utility&gt;
http://www.sgi.com/tech/stl/operators.html
对operator!=的要求是x == y是有效的表达式
对运营商的要求&gt;那是y&lt; x是有效的表达式
对运算符&lt; =的要求是y&lt; x是有效的表达式
运算符&gt; =的要求是x&lt; y是有效的表达式
所以基本上它会自动生成其他运算符给出&lt;和==所有你要做的就是包括&lt; utility&gt;
答案 5 :(得分:1)
我必须说,为了制作一个简单的结构,我们需要考虑很多。
重载运算符&lt;和operator ==你已经完成了。我使用它来编写很多代码,主要是因为我通常要存储的成员变量多于2。
struct MyStruct
{
std::string var1;
std::string var2;
bool var3;
struct less : std::binary_function<struct MyStruct, struct MyStruct, bool>
{
bool operator() (const struct MyStruct& s1, const struct MyStruct& s2) const
{ if (var1== a2.var1) return var2 < a2.var2; else return var3 < a2.var3; }
};
};
typedef std::set<struct MyStruct, MyStruct::less> MySet;
或将这些放在类定义
中bool operator==(const MyStruct& rhs) const
{ return var1 == rhs.var1 && var2 == rhs.var2 && var3 == rhs.var3; };
bool operator<(const MyStruct& a2) const
{ if (var1== a2.var1) return var2 < a2.var2; else return var3 < a2.var3; };
最好的理由是它易于理解上述内容,它们可以轻松地放入类定义中,如果您发现以后需要更多变量,它们很容易扩展。当有一个更简单的解决方案时,我永远不会尝试重载std :: pair。
答案 6 :(得分:1)
不要使用它。
我完全因为这个原因而讨厌std :: pair。你永远不知道哪个是哪个,因为第一个和第二个访问都是公开的,你也不能强制执行合同。
但毕竟,这是一个品味问题。
答案 7 :(得分:0)
不幸的是strong typedef
s不会进入C++0x,它已被赋予未准备好进行C ++ 0x的分类,但未来可以重新提交。< / p>