カスタムハッシャーと比較子を使用してunordered_setに保存したいクラスAがあるとします。このセットを格納するコンテナクラスBもあります。
class B {
private:
std::unordered_set<A, Hasher, Comparer> set;
};
このコンパイルを行うには、Bをテンプレートクラスにする必要があります。これは、いくつかの主要なリファクタリングにつながり、実際にはこの問題を1層上に移動して、Bのテンプレートパラメーターを処理する必要があるためです。
次に、セットに特化したクラスを作成しようとしました。
class MySet1 : public std::unordered_set<A, MyHasher1, MyComparer1> {
};
class MySet2 : public std::unordered_set<A, MyHasher2, MyComparer2> {
};
クラスBのセット変数に共通のベースクラスがまだないので、明らかにそれは役に立ちません。
これを解決するために、順序付けされていないセットを1レベル下に移動しました。
class MySet {
public:
// Some abstract functions...
};
class MySet1 : public MySet {
public:
// Implementation of the abstract functions.
private:
std::unordered_set<A, MyHasher1, MyComparer1> set;
};
class MySet2 : public MySet {
public:
// Implementation of the abstract functions.
private:
std::unordered_set<A, MyHasher2, MyComparer2> set;
};
これで、クラスBに共通の基本クラス(MySet)ができました。しかし、明らかな欠点は次のとおりです。各セットの特殊化に対するコードの重複と、セットをSTLで機能させるためにカスタムイテレーターを実装する必要があります。これが私が立ち止まって、私が実際に望んでいることを達成するためのより良い方法があるかどうかを自問したところです。同じメンバーvarに異なるunordered_setクラスを格納し、そのvarの所有者もテンプレート化する必要はありません。
ここでは、多重継承を喜んで採用できます。
主なアイデアは、セットにタグを付ける基本クラスを作成し、それをすべてのセットの基本クラスにすることです。次に、必要な各テンプレート引数のセットクラスを明示的にインスタンス化し、セットコンテナとタグ付けインターフェイスの両方からパブリックに継承された空のクラスを作成します。そうすれば、追加するものは何もありません。コードの複製は必要ないようです。
とにかく、すべてのテンプレートの特殊化で機能するいくつかの(おそらく仮想)関数を作成する必要があります。保持するものに関係なく、同じコンテキストで単一の変数を使用できる必要があります。ただしusing
、継承のために宣言を増やして一部のコードを減らし、暗黙的な型変換を使用することができます(たとえば、セットに数値のみが含まれている場合)。
#include <set>
class setInterface {
/* Code common for all set template specializations
(you have to have some common interface anyway) */
};
template <typename T> class TSet: public setInterface, std::set<T> {
using std::set<T>::set;
/* more using-declarations or templated versions of some functions
You can use SFINAE here to achieve more magical results,
or use template specializations for specific types. */
};
using intSet = TSet<int>;
using doubleSet = TSet<double>;
class B {
public:
setInterface values;
};
int main () {
B b;
b.values = intSet {1, 2, 3} ;
b.values = doubleSet {1., 2., 3.};
}
PS:構文を使用したテンプレートを提供してくれた@ Jarod42に感謝します。
以下の仮定がなされています。
long long
ます。void*
一般的なケースで使用し、利便性/安全性のためにいくつかの追加メソッドを追加できます。nullptr
sのポインタをチェックする必要はありません(まあ、それは私のコードサンプルにそれ以上の価値をもたらさないでしょう、確かにあなたが常に必要とする現実の世界では)。このソリューションは、非定数の開始/終了を使用し、の新しい光沢のある範囲ベースを使用して、マップを反復処理できます。main
;を参照してください。コンパイルして実行し(-std=c++14
)、結果を確認します。
#include <set>
#include <memory>
#include <iostream>
using common_denominator_type = long long;
class setInterface {
protected:
class iterator_impl;
public:
class iterator {
public:
iterator (iterator_impl* impl) : impl (impl) {}
iterator& operator++ () { ++*impl; return *this; };
bool operator != (const iterator& rhs) const { return *impl != *rhs.impl; };
common_denominator_type operator* () const { return **impl; };
private:
std::shared_ptr <iterator_impl> impl;
};
virtual iterator begin() = 0;
virtual iterator end() = 0;
virtual size_t size() const = 0;
protected:
class iterator_impl {
public:
virtual iterator_impl& operator++ () = 0;
virtual bool operator != (const iterator_impl&) const = 0;
virtual common_denominator_type operator* () const = 0;
virtual void* as_std_set_iterator () = 0;
virtual const void* as_std_set_iterator () const = 0;
};
};
template <typename T> class TSet: public setInterface, std::set<T> {
public:
using std::set<T>::set;
size_t size () const override { return std::set<T>::size(); }
iterator begin () override { return iterator (new TSet<T>::iterator_impl (std::set<T>::begin())); }
iterator end () override { return iterator (new TSet<T>::iterator_impl (std::set<T>::end ())); }
protected:
class iterator_impl: public setInterface::iterator_impl {
public:
using std_it = typename std::set<T>::iterator;
iterator_impl (std_it&& _) : m_real_iterator(std::move (_)) {}
iterator_impl& operator++ () override { ++m_real_iterator; return *this; }
bool operator != (const setInterface::iterator_impl& rhs) const override {
return *reinterpret_cast <const std_it*>(as_std_set_iterator())
!=
*reinterpret_cast <const std_it*>(rhs.as_std_set_iterator());
}
common_denominator_type operator* () const override { return *m_real_iterator; }
void* as_std_set_iterator () override { return &m_real_iterator; }
const void* as_std_set_iterator () const override { return &m_real_iterator; }
private:
std_it m_real_iterator;
};
};
using intSet = TSet<int>;
using longSet = TSet<long>;
class B {
public:
std::shared_ptr <setInterface> values;
};
std::ostream& operator<< (std::ostream& str, B& b) {
str << "[" << b.values->size() << "] [";
for (auto i = b.values->begin(); i != b.values->end(); ++i)
str << *i << " ";
str << "][";
for (auto i : *b.values)
str << i << " ";
return str << "]";
}
int main () {
B b;
b.values.reset (new intSet {1, 2, 3});
std::cout << b << std::endl;
b.values.reset (new longSet {10l, 20l, 30l});
std::cout << b << std::endl;
}
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加