我有以下设计问题,一些管理器类有一个函数,它应该允许扩展一组特定类的类,并将它们添加到一个容器中,该容器保留该特定类集的功能。所以我们有:
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);
}
答案 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_1
和Base_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
。
此实现不要求对象具有多态性。