假设我有一个A类,我希望将其存储在带有自定义hasher和comparer的unordered_set中。我还有一个容器类B来存储这个集合:
class B {
private:
std::unordered_set<A, Hasher, Comparer> set;
};
要进行此编译,我必须使B成为模板类,我想避免,因为这会导致一些重大的重构,并且实际上将此问题移动到一层,然后我必须处理模板参数乙
接下来我尝试创建专门化集合的类:
class MySet1 : public std::unordered_set<A, MyHasher1, MyComparer1> {
};
class MySet2 : public std::unordered_set<A, MyHasher2, MyComparer2> {
};
显然这没有用,因为我仍然没有为B类中的set var提供公共基类。
为了解决这个问题,我将无序设置向下移动了一级:
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)。但明显的缺点是:每个集合特化的代码重复,我必须使用imlement自定义迭代器来使集合与STL一起工作。以下是我停下来问自己是否有更好的方法来实现我真正想要的东西:在同一个成员var中存储不同的unordered_set类,而不需要使该var的所有者也被模板化。
答案 0 :(得分:2)
您可以在这里愉快地使用多重继承。
主要思想是:创建一个标记集合的基类,并使其成为所有集合的基类。然后为您需要的每个模板参数显式实例化set类,创建一个从set容器和标记接口公开继承的空类。那么你将无需添加任何内容,似乎不需要重复代码。
无论如何,您需要创建一些适用于所有模板专业化的(可能是虚拟的)功能。我们需要能够在同一个上下文中使用单个变量,而不管它拥有什么。但是,您可以尝试使用更多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
的指针(好吧,它不会给我的代码示例带来更多价值,确实在您需要的现实世界中)。该解决方案能够使用非const开始/结束并使用新的基于闪亮范围的for来迭代地图。见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;
}