扩展多个类的C ++函数参数

时间:2017-09-14 18:27:56

标签: c++ multiple-inheritance

我有以下设计问题,一些管理器类有一个函数,它应该允许扩展一组特定类的类,并将它们添加到一个容器中,该容器保留该特定类集的功能。所以我们有:

class Base_1 {};
class Base_2 {};

class A :
    public Base_1,
    public Base_2
{

};

class Container 
{
    template<typename T> // where T should extend Base_1 and Base_2
    void Add(T* pObj) {
        elements.push_back(pObj);
    }

    std::vector<???> elements; // all elements should extend Base_1 and Base_2
};

在带有接口的C#中可能有类似的东西,但我无法找到任何方法在c ++中解决这个问题。

我不想创建一个中间类Base_1_2,因为它停止使用3个基类,其中一些管理器需要其元素具有基类1和2,而另一个管理器需要2和3。

所以我的问题是,你如何设计这个?

编辑: 回应: “这闻起来就像一个XY问题” - cdhowie

我可以想象就是这样。所以我会尝试解释'X'问题。

我有一组共享很多功能的类。像“加载到gpu”“渲染”“释放gpu内存”“更新”之类的东西。但并非所有功能都适用于所有类。例如,有些人可能没有“渲染”。现在我有一个类需要存储很多对象,并在调用“render”之前检查“load to gpu”是否已经发生。但是如何实现呢?如何根据这种功能组合来存储类(我可能会再次在这里'Y')。

编辑2:

与cdhowie组成的替代方案类似(但不是通用),以下也是一个解决方案吗? (可能会在Add函数中省略is_base_of)

class MyWrapper
{
public:

    template<class T, typename = std::enable_if_t<
        std::is_base_of<Base_1, T>::value && std::is_base_of<Base_2, T>::value>>
        static MyWrapper MakeWrapper(T* pObj)
    {
        return MyWrapper(dynamic_cast<Base_1*>(pObj), dynamic_cast<Base_2*>(pObj));
    }

    Base_1* as_base_1;
    Base_2* as_base_2;

private:

    MyWrapper(Base_1* p1, Base_2* p2) {
        as_base_1 = p1;
        as_base_2 = p2;
    }
};


class Container
{
public:

    template<class T>
    void Add(T* pObj) {
        elements.push_back(MyWrapper::MakeWrapper<T>(pObj));
    }

    void Update() {
        for (auto& e : elements) {
            e.as_base_1->do_1();
            e.as_base_2->do_2();
        }
    }

    std::vector<MyWrapper> elements; // all elements should extend Base_1 and Base_2
};


void test() {
    A* pA = nullptr;
    Container c;
    c.Add(pA);
}

1 个答案:

答案 0 :(得分:3)

Add不是从这两种类型派生时,您可以停用T

template<typename T>
typename std::enable_if<
    std::is_base_of<Base_1, T>::value &&
    std::is_base_of<Base_2, T>::value,
    void
>::type Add(T* pObj) {
    elements.push_back(pObj);
}

然后,存储在向量中的指针类型没有太大区别,如果 Base_1Base_2是多态的(只需添加一个虚拟析构函数两者) - 然后你可以dynamic_cast得到你需要的任何类型。因此,您可以存储Base_1 *Base_2 *

你也可以有两个向量:一个用于存储Base_1个指针,另一个用于存储Base_2个指针,这并不要求任何一个类型都是多态的。

然而,这似乎是一个特别奇怪的问题需要解决,您可以考虑从设计中退一步看看是否有更好的替代解决方案。

  

我不想创建一个中间类Base_1_2,因为它停止使用3个基类,其中一些管理器需要其元素具有基类1和2,而另一个管理器需要2和3。

这是不真实的。只需在中间类中使用虚拟继承。

作为替代方案,您可以编写一个类型来存储指向所需的所有接口的指针。例如:

#include <type_traits>
#include <tuple>

template <typename, typename...>
struct is_subtype_of_all : std::true_type {};

template <typename Derived, typename Base, typename... BaseTail>
struct is_subtype_of_all<Derived, Base, BaseTail...> : std::conditional<
    std::is_base_of<Base, Derived>::value,
    is_subtype_of_all<Derived, BaseTail...>,
    std::false_type
>::type {};

template <typename...>
struct type_index;

template <typename T, typename... Tail>
struct type_index<T, T, Tail...> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename U, typename... Tail>
struct type_index<T, U, Tail...> : std::integral_constant<std::size_t, 1 + type_index<T, Tail...>::value> {};

template <typename... T>
class interface_wrapper
{
public:
    template <typename U, typename = typename std::enable_if<is_subtype_of_all<U, T...>::value>::type>
    explicit interface_wrapper(U *p)
        : pointers{static_cast<T *>(p)...} {}

    template <typename U>
    U * get() const
    {
        return std::get<type_index<U, T...>::value>(pointers);
    }

private:
    std::tuple<T *...> pointers;
};

现在,您可以将interface_wrapper<Base_1, Base_2>存储在向量中,并且可以在包装器对象上使用.get<Base_1>()->some_member_of_Base_1

此实现不要求对象具有多态性。