【问题标题】:How to casting class template based on template argument type如何根据模板参数类型转换类模板
【发布时间】:2019-11-10 12:52:13
【问题描述】:

目前,我将不同类型的指针存储在向量中。为了归档这个,我实现了一个类模板“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。存储 模板类对象并将此对象的指针插入到 m_storedObjects。

现在开始棘手的部分:方法 Main::callObjects 应该由其他类使用模板参数调用,例如 Main::callObjects()。这应该遍历 m_storedObjects 并为每个对象调用 IStore::call 方法,该对象属于 B 类型或 B 派生自哪个类型。

例如:

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中的cast操作符,这样当模板参数是可转换的时,我可以指定Store应该是可转换的。例如在商店中:

template<typename C>
operator Store<C>*()
{
    if(std::is_convertible<T, C>::value)
    {
        return this;
    }
    else
    {
        return nullptr;
    }
}

但是这个方法永远不会被调用。

有没有人能解决这个问题? 很抱歉,这篇文章很长,但我认为更多的代码会更好地理解这个问题。 无论如何感谢您的帮助:)

【问题讨论】:

标签: c++ c++11 templates casting


【解决方案1】:

经过一番思考,我意识到您的类型擦除,从将 Store&lt;T&gt; 对象分配给 IStore* 指针,使得无法使用任何编译时类型检查,如 std::is_base_of 等。您拥有的下一个最佳选择是运行时类型信息(dynamic_cast&lt;&gt;()typeid())。正如您所观察到的,dynamic_cast&lt;&gt;() 无法确定对象的类型是否是另一种类型的 祖先,只有当对象的类型是编译时已知的另一种类型的后代时。

编辑:借助 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; 
}

【讨论】:

    猜你喜欢
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 2014-01-29
    • 2021-03-08
    相关资源
    最近更新 更多