13

std::pair を使用して、関連する 2 つの数量の論理グループを関数の引数/戻り値として定義することがよくあります。いくつかの例: 行/列、タグ/値など。

多くの場合、std::pair を使用するだけでなく、実際に独自のクラスを展開する必要があります。物事が崩壊し始める時期を確認するのは非常に簡単です。コードが make_pair で散らばり、最初と 2 番目に何が何であるかを思い出すのが非常に難しくなり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 に格納する場合、first と second にアクセスできないため、operator< オーバーロードにアクセスできなくなりました。これは、必要な演算子のオーバーロードごとに転送関数を定義する必要があることを意味します。私にとって、これは通常 ==、!=、および < ですが、他にも必要な場合があります。はい、連想コンテナーに貼り付けるためだけに 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

同僚が私に2つの可能な解決策を指摘しました。

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

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 に答える
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 に答える
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がまさにこの理由で嫌いです。どちらがどちらであるかはわかりません。また、1番目と2番目へのアクセスは公開されているため、契約を強制することもできません。

しかし結局のところ、それは好みの問題です。

于 2009-06-09T22:50:32.367 に答える
0

残念ながら、strong typedefsC++0xにはなりません。C++0x の準備ができていませんが、将来的に再提出できるように分類されています。

于 2008-10-28T10:10:30.980 に答える