13

我经常发现自己使用 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

所以现在我们有了简单的变量访问,但是现在定义重载的操作符更加痛苦,因为不是仅仅将它们转发到对的实现,我们实际上每次都必须重新实现它们......

有没有我忽略的解决方案可以让这件事变得简单而没有缺点?如果没有,你会倾向于哪个?

4

8 回答 8

6

这就是Boost.Tuple的用途。

但是您现在可能应该使用std::tuple ...

于 2008-10-14T18:50:21.740 回答
4

一位同事向我指出了两种可能的解决方案:

使用boost strong typedef作为 typedef 的改进版本。我以前从未听说过这个,而且它似乎并不是任何子库的一部分,只是有点浮动。

使用宏生成不同运算符所需的代码。这样我就不必在每个定义级别上显式编写任何内容,只需执行类似DEFINE_PAIR_TYPE(Position, int, int, row, col);. 这可能最接近我正在寻找的东西,但与其他人提出的一些解决方案相比,它仍然感觉有点邪恶。

于 2008-10-14T19:22:48.307 回答
3

还有Boost::Operators库可以自动生成操作符代码。它类似于Martin York 建议的 SGI 库,但可能更便携。

于 2008-10-15T02:54:29.160 回答
2

您可以使用一些有助于定义关系运算符的标准实用程序模板。

#include <实用程序>

http://www.sgi.com/tech/stl/operators.html

类型要求

operator!= 的要求是 x == y 是一个有效的表达式
operator> 的要求是 y < x 是一个有效的表达式
operator<= 的要求是 y < x 是一个有效的表达式
operator> 的要求= 是 x < y 是一个有效的表达式

所以基本上它会自动生成其他运算符给 < 和 == 你所要做的就是包含 <utility>

于 2008-10-14T19:08:02.783 回答
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 );
}

尽管您最终仍然需要为您需要的每个手术室都这样做......

于 2008-10-14T18:52:57.790 回答
1

我必须说,为了制作一个简单的结构,需要花很多心思。

重载 operator< 和 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 。

于 2008-10-15T22:25:40.497 回答
1

不要使用它。

正是出于这个原因,我讨厌 std::pair 。您永远不知道哪个是哪个,并且由于对 first 和 second 的访问是公开的,因此您也无法执行合同。

但毕竟,这是一个品味问题。

于 2009-06-09T22:50:32.367 回答
0

不幸的是strong typedefs不会进入C++0x,它已被归类为Not ready for C++0x, but open to resubmit in future

于 2008-10-28T10:10:30.980 回答