c ++异构容器,输入类型

时间:2017-12-12 08:42:53

标签: c++ templates containers heterogeneous-array

我有以下简单的异构容器实现:

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);
}

我可以添加任何类型的条目。但我无法弄清楚如何实现一个函数来检索元素,作为某种类型,它可能与条目或基类相同。

我需要的东西似乎同时是虚拟和模板,这是不可能的。

2 个答案:

答案 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";
}