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
これで変数へのアクセスは簡単になりましたが、オーバーロードされた演算子を定義するのはさらに面倒です。なぜなら、それらをペアの実装に転送するだけでなく、実際に毎回再実装する必要があるからです...
欠点なしでこれを簡単にする、私が見落とした解決策はありますか? そうでない場合、どちらを好む傾向がありますか?