我尝试在需要擦除的基础上实现适合动态和静态多态性的类,即仅在必须时才使用动态多态性(例如异构列表)。我写了以下代码
class Connection {
public:
Connection& operator=(Connection& c) = delete;
Connection(Connection& c) = delete;
Connection& operator=(Connection&& c) = delete;
Connection(Connection&& c) = delete;
virtual ~Connection() = 0;
virtual string Recv() = 0;
}
template<typename T>
class IConnection : Connection{
public:
inline T* dispatch(){
return static_cast<T*>(this);
}
virtual string Recv() override{
return dispatch()->Recv();
}
...
}
但是,如果我要实现
class Socket :IConnection<Socket>
,那么Recv()
中的class Socket
仍然是动态调度。
有没有解决方法,以便我可以使用
IConnection<Socket> s{Socket{...}}; // statically
Connection c{Socket{...}}; //dynamically
当您继承
Connection
时,您的问题就开始了。
您将 Connection
定义为抽象类,这意味着编译器将生成一个 vtable 和所有这些有趣的玩具。Connection*
的事物期望 vtable 等。Connection
界面有时是静态的。这意味着调用者将不得不改变他们调用内容的方式。但这是两种截然不同的类型,具有两种截然不同的调用约定。您可以做的是一个模板化的
Connection
类,它接收一个类型并假设该类型具有您期望的函数,并在具有相同名称的函数中调用它们。您将无法将其传递给需要 Connection*generically
的东西,但它可能仍然适合您的需求。
template <typename T>
class Connection {
public:
Connection(T&& impl) : _impl(impl) {}
std::string recv() { return _impl.recv(); }
... rest of your implementation...
private:
T impl;
};
这样,如果
foo
是实现某个需要 recv()
的接口的对象,Connection<foo>
将使用 vtable,并且您将拥有动态调度。如果您构造一个 Connection<bar>
,其中 bar 是一个普通类,恰好有一个 recv()
方法,它仍然可以工作,并且您将获得静态调度。