当前,我将不同类型的指针存储在向量中。为了对此进行归档,我实现了一个类模板“ Store”,该类模板派生自非类模板“ IStore”。我的向量最终存储了指向“ IStore”的指针。 在代码中:
class IStore
{
public:
IStore() = default;
virtual ~IStore() = default;
virtual void call() = 0;
// ... other virtual methods
};
template<typename T>
class Store : public IStore
{
public:
Store() = default;
virtual ~Store() = default;
virtual void call() override;
// ... other virtual methods
private:
T* m_object = nullptr;
}
在我的主类中有向量:
class Main
{
public:
template<typename T>
void registerObject(T* ptr);
template<typename T>
void callObjects();
// ... other methods
private:
std::vector<IStore*> m_storedObjects;
};
到目前为止,当前的类结构。为了描述这个问题,我需要介绍以下三个示例结构:
struct A {}
struct B : public A {}
struct C : {}
其他类应使用指向A,B或C类型对象的指针来调用Main :: registerObject方法。然后,此方法将创建一个新的Store 和Store 。 Store
现在,棘手的部分开始了:Main :: callObjects方法应该由其他带有模板参数的类调用,例如Main :: callObjects ()。这应该遍历m_storedObjects并为每个对象(类型为B或类型B派生)调用IStore :: call方法。
例如:
Main::registerObject<A>(obj1);
Main::registerObject<B>(obj2);
Main::registerObject<C>(obj3);
Main::callObjects<B>();
应该调用obj1和obj2而不是obj3,因为C不是B,并且B不是从C派生的。
我在Main :: callObjects中的方法是: 1.执行dynamic_cast并对照nullptr进行检查,例如:
for(auto store : m_storedObjects)
{
Store<T>* base = dynamic_cast<Store<T>*>(store);
if(base)
{
// ...
}
}
仅适用于相同的类,不适用于派生类,因为Store 不是派生自Store 。 2.要覆盖IStore和Store中的强制转换运算符,以便当template参数可转换时,我可以指定Store应该可转换。例如在商店中:
template<typename C>
operator Store<C>*()
{
if(std::is_convertible<T, C>::value)
{
return this;
}
else
{
return nullptr;
}
}
但是永远不会调用此方法。
有人可以解决这个问题吗? 抱歉,很长的帖子,但我认为更多的代码会更好地理解问题。 无论如何,谢谢您的帮助:)
答案 0 :(得分:0)
经过一番思考,我意识到从将Store<T>
对象分配给IStore*
指针到您的类型擦除,使得不可能使用任何编译时类型检查,例如std::is_base_of
等。 。下一个最佳选择是运行时类型信息(dynamic_cast<>()
,typeid()
)。如您所见,dynamic_cast<>()
不能确定对象的类型是否是另一种类型的祖先,仅当对象的类型是在编译时已知的另一种类型的后代。
编辑:借助C ++ 17支持,我可以根据std::visit
示例here考虑另一种解决您的问题的方法。如果您更改Main
界面...
#include <iostream>
#include <vector>
#include <variant>
template <typename T>
class Store {
public:
using value_type = T;
Store(T* object): m_object(object) {}
void call() { std::cout << "Hello from " << typeid(T).name() << '\n'; }
// ... other methods
private:
T* m_object = nullptr;
};
template <typename... Ts>
class Main {
private:
std::vector<std::variant<Store<Ts>...>> m_storedObjects;
public:
// replacement for registerObjects, if you can take all objects in at once
Main(Ts*... args): m_storedObjects({std::variant<Store<Ts>...>(Store<Ts>{args})...}) {}
template <typename U>
void callObjects() {
for (auto& variant : m_storedObjects) {
std::visit([](auto&& arg) {
using T = typename std::decay_t<decltype(arg)>::value_type;
if constexpr (std::is_base_of<T, U>::value) {
arg.call();
}
}, variant);
}
}
};
struct A {};
struct B : public A {};
struct C {};
int main() {
A a;
B b;
C c;
auto m = Main{&a, &b, &c};
m.callObjects<B>();
// > Hello from 1A
// > Hello from 1B
return 0;
}