首页 > 解决方案 > 如何确保 C++ 中的运行时类型协方差?

问题描述

假设我有一个接口类和模板子类

struct IPort {
    virtual bool can_connect_to(std::shared_ptr<IPort> p) = 0;
    std::shared_ptr<IPort> receiver;
};

template <class T>
struct Port : IPort {
    bool can_connect_to(std::shared_ptr<IPort> p) override {
        // what goes here?
    }

    void send(T* t) {
        auto p = std::static_pointer_cast<Port>(receiver);
        p->on_recv(t);
    }

    std::function<void(T*)> on_recv;
};

模板参数可以是任何类型。我有一些基本类型(BaseOther以下)以及派生类型(Derived)。

struct Base {
    virtual const char* get_data() = 0;
    virtual size_t get_size() = 0;
};

struct Derived : public Base {
    std::string str;
    const char* get_data() override { return str.c_str(); }
    size_t get_size() override { return str.size(); }
};

struct Other {
    double x, y, z;
};

理想情况下,我应该能够将派生类型从一个端口发送到另一个接受基本类型的端口(例如Port<Derived>应该能够连接到Port<Base>)。另一方面,我不应该能够发送不兼容的类型(Port<Other>不应该能够连接到Port<Base>)。

auto sender1 = std::make_shared<Port<Derived>>();
auto sender2 = std::make_shared<Port<Other>>();
auto receiver = std::make_shared<Port<Base>>([](Base* t) {
    // do something with t
});

sender1->can_connect_to(receiver); // should return true
sender2->can_connect_to(receiver); // should return false

我可以输入什么can_connect_to来验证传入的端口是否具有模板化类型,它是发送端口模板的超类?

另外,为什么static_pointer_castinsend工作?它似乎做了正确的事情,但我不明白它是如何有效的 c++,因为Port.

标签: c++covariance

解决方案


在运行时测试对象是否“是 A”某个类的基本方法是dynamic_cast. 所以我认为你需要:

template <class T>
bool Port<T>::can_connect_to(std::shared_ptr<IPort> p) {
    return dynamic_cast<T*>(p.get());
}

如果p是空指针,这也将返回 false,这可能是您想要的。

另外,为什么static_pointer_castinsend工作?它似乎做了正确的事情,但我不明白它是如何有效的 c++,因为Port.

在类模板的定义中,或其任何成员(或其嵌套类成员的成员等)的定义中,模板的名称可以用作使用该定义的模板参数的类类型的别名. 也就是说,由于使用Port出现在定义template <class T> struct Port {中 ... };,没有模板参数列表的名称PortPort<T>. 这称为“注入的类名”。


推荐阅读