我正在通过其 C++ SDK 为应用程序编写插件。该机制相当简单。插件通过预定义的接口(interface)提供其功能。这是通过让服务器类从每个接口(interface)的一个实现类继承来完成的,该实现类包含纯虚函数或具有默认实现的非纯函数。
这非常实用,因为 SDK 客户端只需覆盖插件所需的那些方法和/或为(罕见的)没有默认值的方法提供实现。
一直困扰我的是,一切都在编译时已知。与运行时多态相关的虚函数表和机制在这里只是为了提供默认实现。
我试图在保持便利的同时消除这种开销。
作为一个(非常人为的)示例,假设我有几个服务器提供一个接口(interface)(名为 Blah),该接口(interface)仅包含一个没有默认实现的方法。
// SDK header
struct OldImpl_Blah {
virtual ~OldImpl_Blah() =default;
virtual int mult(int) =0;
};
// plugin source
class OldServer3 : public OldImpl_Blah {
public:
int mult(int i) override { return 3 * i; }
};
class OldServer5 : public OldImpl_Blah {
public:
int mult(int i) override { return 5 * i; }
};
对于纯虚函数,直接 CRTP 工作得很好。
// SDK header
template <typename T>
struct NewImpl_Blah {
int mult(int i) { return static_cast<T*>(this)->mult(i); }
};
// plugin source
class NewServer3 : public NewImpl_Blah<NewServer3> {
public:
int mult(int i) { return 3 * i; }
};
class NewServer5 : public NewImpl_Blah<NewServer5> {
public:
int mult(int i) { return 5 * i; }
};
问题在于非纯虚函数,即当方法有默认实现时。
// SDK header
struct OldImpl_Blah {
virtual ~OldImpl_Blah() =default;
virtual int mult(int i) { return i; } // default
};
// plugin source
class OldServer3 : public OldImpl_Blah {
public:
int mult(int i) override { return 3 * i; }
};
class OldServer5 : public OldImpl_Blah {
public:
int mult(int i) override { return 5 * i; }
};
我试图将 CRTP 与一些表达 SFINAE 技巧结合起来,但失败了。
我想我需要的是某种代码调度,其中基类要么提供默认实现,要么将其参数转发给派生类中的实现(如果存在)。
问题似乎是分派(dispatch)应该依赖于基类中编译器尚不可用的信息。
一个简单的解决方案是删除代码中的 virtual
和 override
关键字。但是编译器不会检查函数签名是否匹配。
这种情况有一些众所周知的模式吗?我问的是可能的吗?
(请使用小字,因为我对模板的专业知识有点浅。谢谢。)
最佳答案
与往常一样,另一个间接级别是解决方案。在这种特殊情况下,这是众所周知的公共(public)非虚函数调用私有(private)或 protected 虚函数的技术。它有自己的用途,与这里讨论的内容无关,所以无论如何都要检查一下。通常它是这样工作的:
struct OldImpl_Blah {
piblic:
virtual ~OldImpl_Blah() = default;
int mult(int i) { return mult_impl(i); }
protected:
virtual int mult_impl(int i) { return i; }
};
// plugin source
class OldServer3 : public OldImpl_Blah {
protected:
int mult_impl(int i) override { return 3 * i; }
};
对于 CRTP,它完全一样:
template <class T>
struct OldImpl_Blah {
piblic:
virtual ~OldImpl_Blah() = default;
int mult(int i) { return static_cast<T*>(this)->mult_impl(i); }
protected:
virtual int mult_impl(int i) { return i; }
};
// plugin source
class OldServer3 : public OldImpl_Blah<OldServer3> {
protected:
int mult_impl(int i) override { return 3 * i; }
};
免责声明:CRTP 据说通过要求函数是虚拟
来消除虚拟调用开销。我不知道当函数保持 virtual
时 CRTP 是否有任何性能优势。
关于c++ - 用 CRTP 替换非纯虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36270985/