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

C++ function parameter that extends multiple classes

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

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++ 中解决此问题的任何方法。

我不想创建一个中间 class Base_1_2 因为它停止使用 3 base classes 一些经理需要它的元素有 base class es 1 和 2,另一个经理需要 2 和 3。

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


编辑: 回应: "This smells like an XY problem" – cdhowie

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

我有一组共享很多功能的 classes。像 "load to gpu" "render" "release gpu memory" "update" 这样的东西。但并非所有功能都适用于所有 classes。例如,有些可能没有 "render"。现在我有一个 class 需要存储很多对象并在调用 "render" 之前检查 "load to gpu" 是否已经发生。但是如何实现呢?你如何根据这种功能组合存储 classes(我可能会再次向 '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);
}

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

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

那么你存入vector的指针类型没有太大区别,if Base_1Base_2是多态的(只要加一个virtual destructor to both) - 然后你可以 dynamic_cast 到你需要的任何类型。所以你可以存储 Base_1 *Base_2 *.

您还可以有两个向量:一个用于存储 Base_1 指针,一个用于存储 Base_2 指针,这不需要任何一种类型都是多态的。

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


I do not want to create an intermediate class Base_1_2 because that stops working with 3 base classes where some manager needs its elements to have base classes 1 and 2, and another manager needs 2 and 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

此实现不要求对象是多态的。