2017-09-14 88 views
0

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

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++来解决这个问题。

我不想因为停止与其中一些经理需要它的元素有基类1和2 3分基类的工作,以创建一个中间类Base_1_2,和另一个经理需要2个和3

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


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

我能想象这样的话。所以我会试着解释'X'的问题。

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


编辑2:

类似(但不通用)被cdhowie组成的替代,以下将是一个解决方案吗? (也许留出上添加功能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); 
} 
+4

这味道像[XY问题](https://meta.stackexchange.com/a/233676/218910)。 – cdhowie

+0

扩展Base_1和Base_2的类的定义是什么? –

+0

根据您的额外解释,有可能的解决方案的实现是丑陋的,但其用法是优雅的 - 您有一个包装类型,确保包装的对象继承你需要的所有类型,* that *就是你存储的在向量中。 – cdhowie

回答

3

您可以禁用AddT不是从两种类型的衍生:

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,这不需要任何一种类型是多态的。

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


我不想因为停止与其中一些经理需要它的元素有基类1和2 3分基类的工作,以创建一个中间类Base_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

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

+0

我想用IContainer和函数“Add”来检查对象是否有适当的基类,然后用其中一个基类调用虚函数“AddImpl”。所以我需要创建一个基于一个基类的包装。我怎样才能做到这一点? – Aedoro

+0

如果您使用我的代码,只需指定一个模板参数。它可以根据需要采用尽可能多的模板参数,并且强制传递给构造函数的指针的类型派生自类模板参数中的所有类型。 – cdhowie

+0

是的静态执法是问题。 如果我需要扩展所有的B1,B2,B3。 IContainer检查'x'扩展B1 B2 B3,然后调用AddImpl(B1 *)(这是一个虚函数,因此不能保留'x'的类型)。但是接下来包装器需要扩展B2和B3的东西,我只有一个指向B1 *的指针,尽管我知道它也通过设计扩展了B2和B3(不是静态强制的)。 – Aedoro