3

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

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

我可以向其中添加任何类型的条目。但是我不知道如何实现一个函数来检索元素,作为某种类型,它可能与它的条目或基类相同。

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

4

2 回答 2

2

如何实现一个函数来检索元素,作为某种类型,它可能与它的条目或基类相同。

要获得相同的条目,保留当前设计的最简单方法是使用 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>,...
}

或使用其他一些非侵入性方式来注册目标类型(例如特征类)。

否则,我认为目前这通常是不可能的(这将是我们进行编译时反射的时候)。

于 2017-12-12T08:49:09.810 回答
2

如果没有太多的痛苦和不便(例如,在某种中央存储库中注册您想要使用的所有类),似乎不可能完全拥有您想要的东西。

这是一种几乎可以做你想做的事情的方法,它可能有用。

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";
}
于 2017-12-12T16:14:12.043 回答