c ++异构容器,获取条目作为类型
c++ heterogeneous container, get entry as type
我有以下异构容器的简单实现:
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);
}
我可以向其中添加任何类型的条目。但是我不知道如何实现一个函数来检索元素,作为某种类型,它可能与条目或基数相同class。
我需要的好像同时是虚拟和模板,这是不可能的。
how to implement a function to retrieve elements, as some type, which may be the same as the entry or a base class to it.
要获得相同的条目,保留当前设计的最简单方法是使用 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;
}
每当 h_[i]
指向的对象的动态类型是错误的类型时,这将 return nullptr
。您也可以抛出或提供抛出 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>,...
}
或使用其他一些非侵入性方法来注册目标类型(例如特征 class)。
否则,我认为目前通常不可能(这将是我们进行编译时反射时)。
似乎不可能 完全 您想要的东西而不会有太多痛苦和不便(例如,注册所有 类 您想要使用的东西一种中央存储库)。
这是一种几乎您想要的方法,可能会有用。
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";
}
我有以下异构容器的简单实现:
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);
}
我可以向其中添加任何类型的条目。但是我不知道如何实现一个函数来检索元素,作为某种类型,它可能与条目或基数相同class。
我需要的好像同时是虚拟和模板,这是不可能的。
how to implement a function to retrieve elements, as some type, which may be the same as the entry or a base class to it.
要获得相同的条目,保留当前设计的最简单方法是使用 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;
}
每当 h_[i]
指向的对象的动态类型是错误的类型时,这将 return nullptr
。您也可以抛出或提供抛出 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>,...
}
或使用其他一些非侵入性方法来注册目标类型(例如特征 class)。
否则,我认为目前通常不可能(这将是我们进行编译时反射时)。
似乎不可能 完全 您想要的东西而不会有太多痛苦和不便(例如,注册所有 类 您想要使用的东西一种中央存储库)。
这是一种几乎您想要的方法,可能会有用。
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";
}