我有以下简单的异构容器实现:
struct Container {
struct HolderBase {
};
template<typename S>
struct Holder : HolderBase {
Holder(S* s) : s_(s) {}
S* s_;
};
template<typename S>
void push_back(S* s) {
h_.push_back(new Holder<S>(s));
}
vector<HolderBase*> h_;
template<typename B>
B* get(int i) {
//magic here
}
};
以下是如何使用它:
struct ElementBase {};
struct Element : ElementBase {};
int main()
{
Container container;
container.push_back(new Element);
ElementBase* elementBase = container.get<ElementBase>(0);
}
我可以添加任何类型的条目。但我无法弄清楚如何实现一个函数来检索元素,作为某种类型,它可能与条目或基类相同。
我需要的东西似乎同时是虚拟和模板,这是不可能的。
答案 0 :(得分:1)
如何实现一个函数来检索元素,作为某种类型,可能与它的条目或基类相同。
要获得相同的条目,保留当前设计的最简单方法是使用RTTI。
首先,使类型擦除基多态:
struct HolderBase { virtual ~HolderBase() = default; };
然后,你可以只是dynamic_cast:
template<typename B>
B* get(int i) {
if( auto holder = dynamic_cast<Holder<B>*>(h_[i]) )
{
return holder->s_;
}
else
return nullptr;
}
每当nullptr
指向的对象的动态类型为错误类型时,将返回h_[i]
。您也可以抛出或提供抛出get<B&>
重载。
请注意,在C ++ 17中,我们已经有std :: any(源自boost.any)基本上做同样的事情,但是使用标准接口(很快就会成为惯用语)并且所有细节都已经解决了;所以,强烈建议使用它而不是推出自己的。
以入境为基础的问题更难了;最简单的解决方案是将允许的目标类型传递给pushback,例如:
template<typename... T,typename S>
void push_back_as(S* s) {
static_assert( ( std::is_base_of_v<T,S> && ...) );
h_.push_back(new Holder<S,T...>(s)); // where Holder<S,T0,...> inherits from properly defined Holder<S>,Holder<T0>,...
}
或使用其他一些非侵入性手段来注册目标类型(例如,特质类)。
否则,我认为现在通常是不可能的(当我们有编译时反射时)。
答案 1 :(得分:1)
似乎没有完全你想要的东西没有太多的痛苦和不便(例如,注册你想要在某种中央存储库中使用的所有类)。
这是几乎你想要什么可能有用的一种方法。
class HolderBase
{
public:
virtual ~HolderBase() = default;
template <class X> X* get() { return dynamic_cast<X*>(this); }
};
template <class T>
class Holder : public HolderBase, public T
{
public:
using T::T;
};
您的容器只是vector<unique_ptr<HolderBase>>
或您想要的任何一堆指针。
试驾:
struct A {
virtual ~A() = default;
A(int a) : a(a) {};
int a;
};
struct B : A {
B(int a, int b) : A(a), b(b) {};
int b;
};
struct C : A {
C(int a, int c) : A(a), c(c) {};
int c;
};
int main () {
std::vector<std::unique_ptr<HolderBase>> v;
v.emplace_back(std::make_unique<Holder<B>>(7,40));
v.emplace_back(std::make_unique<Holder<C>>(0,42));
A* a = v[0]->template get<A>();
B* b = v[0]->template get<B>();
C* c = v[0]->template get<C>();
std::cout << a << " " << b << " " << c << "\n";
a = v[1]->template get<A>();
b = v[1]->template get<B>();
c = v[1]->template get<C>();
std::cout << a << " " << b << " " << c << "\n";
}