扩展多个 类 的 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_1
和Base_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
。
此实现不要求对象是多态的。
我有以下设计问题,一些管理器 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_1
和Base_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
。
此实现不要求对象是多态的。