有没有一种方便的方法将 std::pair 包装为新类型?

发布于 2024-07-07 10:31:01 字数 2381 浏览 4 评论 0原文

我经常发现自己使用 std::pair 将两个相关量的逻辑分组定义为函数参数/返回值。 一些例子:行/列、标签/值等。

很多时候我真的应该滚动我自己的类,而不是仅仅使用 std::pair。 当事情开始崩溃时,很容易看出 - 当代码中充斥着 make_pair、first 和 secondary 时,很难记住什么是什么 - std::pair传达的含义比 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 中时,我们现在无法访问运算符< 过载,因为我们无法访问第一个和第二个。 这意味着我们必须为我们想要的每个运算符重载定义一个转发函数。 对我来说,这通常是 ==、!= 和 <,但可能还有其他我想要的。 是的,我知道我可能不应该重载运算符< 只是将它放在关联容器中,但这使一切变得非常简单......并且为每个新类型定义这些运算符是一件痛苦的事情,我们仍然必须通过函数进行访问。 解决这个问题:

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

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

我们可以 有没有我忽略的解决方案可以让这件事变得简单而又没有缺点? 如果没有,您会更喜欢哪个?

Often times I find myself using std::pair to define logical groupings of two related quantities as function arguments/return values. Some examples: row/col, tag/value, etc.

Often times I should really be rolling my own class instead of just using std::pair. It's pretty easy to see when things start breaking down - when the code becomes littered with make_pair, first, and second, its very hard to remember what is what - an std::pair<int, int> conveys less meaning than a type Position.

What have you found are the best ways to wrap the functionality of std::pair in a type that conveys real meaning?

Here are some things I have considered:

typedef std::pair<int, int> Position;

This at least gives the type a meaningful name when passing it around, but the type isn't enforced, its still really just a pair, and most of the same problems still exist. This is however very simple to write.

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; }
};

This is better, since we can access the variables via a reasonably descriptive name. The problem here is that you can still access first and second, so its easy for the abstraction to leak. Also, accessing simple variables via functions makes the syntax annoying.

The obvious next step is to make the inheritance private:

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...
};

So now at least we have gotten rid of the access to first and second, but now a new problem pops up. When we want to store the type in an std::set, we now don't have access to the operator< overload since we don't have access to first and second. This means we have to define a forwarding function for each operator overload we want. For me this is usually ==, !=, and <, but there could be others that I'd want. Yes I know I probably shouldn't overload operator< just to stick it in an associative container, but it makes everything so darn simple... And defining these operators for each new type is a pain, and we STILL have to access via functions. We can fix that:

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

So now we have simple variable access, but now defining overloaded operators is even more of a pain, because instead of just forwarding them to the pair's implementation, we actually have to re-implement them each time...

Are there any solutions I have overlooked that make this easy without the drawbacks? If there aren't which would you tend to prefer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

疯了 2024-07-14 10:31:01

这就是 Boost.Tuple 的制作过程为了。

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

This is what Boost.Tuple was made for.

But you should probably be using std::tuple now...

谁把谁当真 2024-07-14 10:31:01

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

使用 boost Strong typedef 作为 typedef 的改进版本。 我以前从未听说过这个,而且它似乎并不真正属于任何子图书馆,只是一种浮动的。

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

A coworker pointed me to two possible solutions:

Using boost strong typedef as an improved version of the typedef. I'd never heard of this before, and it doesn't seem to really be part of any sub-library, just kind of floating.

Using a macro to generate the code needed for the different operators. This way I wouldn't have to explicitly write anything on a per definition level, just do something like DEFINE_PAIR_TYPE(Position, int, int, row, col);. This is probably closest to what I'm looking for, but it still feels kind of evil compared to some of the solutions presented by others.

寻梦旅人 2024-07-14 10:31:01

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

There's also the Boost::Operators library to automatically generate operator code. It's similar to the SGI library that Martin York suggested, but might be more portable.

探春 2024-07-14 10:31:01

您仍然可以通过转发来重用 pair 功能:

bool operator< ( const Position &a, const Position &b ) 
{
    return
        std::make_pair( a.row, a.col ) < std::make_pair( b.row, b.col );
}

尽管您最终仍然需要为您需要的每个操作员执行此操作...

You can still reuse the pair functionality by forwarding to it:

bool operator< ( const Position &a, const Position &b ) 
{
    return
        std::make_pair( a.row, a.col ) < std::make_pair( b.row, b.col );
}

Although you still end up with doing this for every operatory you need...

眼波传意 2024-07-14 10:31:01

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

#include <实用程序>

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

类型要求

运算符 != 的要求是 x == y 是一个有效的表达式
对操作人员的要求> 是 y < x 是一个有效的表达式
运算符 <= 的要求是 y < x 是一个有效的表达式
运算符>=的要求是x < y 是一个有效的表达式

所以基本上它会自动生成其他运算符给出
并且 == 您所要做的就是包含;

You can use some standard utility templates that help define the relation operators.

#include <utility>

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

Requirements on types

The requirement for operator!= is that x == y is a valid expression
The requirement for operator> is that y < x is a valid expression
The requirement for operator<= is that y < x is a valid expression
The requirement for operator>= is that x < y is a valid expression

So basically it will automatically generate the other operators give < and == all you have to do is include <utility>

洛阳烟雨空心柳 2024-07-14 10:31:01

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

重载运算符< 和运算符 == 就完成了。 我在编写的很多代码中都使用它,主要是因为我通常要存储的成员变量多于 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 。

I must say that's a lot of thought just to make a simple struct.

Overload operator< and operator== and you're done. I use that for a lot of code I write, mainly because I usually have more member variables to store than 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;

or put these inside the class definition

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; };

The best reasons are that its easy to understand the above, they can be slipped into the class definition easily, and they are easy to expand if you find you need more variables later on. I would never try to overload std::pair when there's a much simpler solution.

南…巷孤猫 2024-07-14 10:31:01

不要使用它。

正是因为这个原因,我讨厌 std::pair 。 你永远不知道哪个是哪个,而且由于对第一个和第二个的访问是公开的,你也无法执行合同。

但归根结底,这是一个品味问题。

Don't use it.

I hate std::pair exactly for this reason. You never know which is which, and since access to first and second are public you can't enforce contracts either.

But after all, it's a matter of taste.

路还长,别太狂 2024-07-14 10:31:01

不幸的是typedef s 不会进入 C++0x,已被赋予分类“尚未准备好用于 C++0x,但开放以在将来重新提交”。

Unfortunately strong typedefs will not make it into C++0x, it has been given the classification of Not ready for C++0x, but open to resubmit in future.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文