【问题标题】:Is there a convenient way to wrap std::pair as a new type?有没有一种方便的方法可以将 std::pair 包装为新类型?
【发布时间】:2008-10-14 18:48:08
【问题描述】:

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

很多时候我真的应该滚动我自己的课程,而不是仅仅使用 std::pair。很容易看出事情何时开始崩溃 - 当代码中到处都是 make_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; }
};

这样更好,因为我们可以通过一个合理的描述性名称来访问变量。这里的问题是您仍然可以访问 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

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

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

是否有任何我忽略的解决方案可以让这件事变得简单而没有缺点?如果没有,你会更喜欢哪一个?

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    这就是 Boost.Tuple 的用途。

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

    【讨论】:

    • 我也许可以为 get 参数创建一个枚举并以相同的方式包装它,但似乎这会过多地污染命名空间范围,特别是如果我需要类似的类型。
    • 如果你把这些对象放在它们自己的命名空间中会怎样?控制污染。
    • 我想,但是为单独的类型创建单独的命名空间会污染使用它们的代码。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        虽然你最终还是要为你需要的每一个手术室都这样做......

        【讨论】:

          【解决方案5】:

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

          #include

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

          类型要求

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

          所以基本上它会自动生成其他运算符给

          【讨论】:

          • 这个问题是我必须在我的代码中放置一个“使用命名空间 std::rel_ops”......这可能不是一个糟糕的解决方案,但我想避免它。这也意味着我仍然必须定义 operator
          • 如果您使用的是 C++ 标准库,请优先考虑有关 C++ 标准库的文档。
          【解决方案6】:

          我必须说,仅仅为了制作一个简单的结构就需要考虑很多。

          重载 operator

          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。

          【讨论】:

            【解决方案7】:

            不要使用它。

            正是因为这个原因,我讨厌 std::pair。你永远不知道哪个是哪个,而且由于对 first 和 second 的访问是公开的,你也不能强制执行合同。

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

            【讨论】:

              【解决方案8】:

              很遗憾strong typedefs 不会进入C++0x,它已被归类为尚未为 C++0x 做好准备,但可以在未来重新提交

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-05-11
                • 2011-08-24
                • 1970-01-01
                • 2011-09-24
                • 1970-01-01
                相关资源
                最近更新 更多