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