我正在创建一个库,该库要求类必须继承其他类才能做特定的事情。但是,这不是简单的多态性。这些类是虚拟函数的代码生成器,它们没有数据并且依赖于CRTP,因此它们本身不需要vtable。
是否有一种方法可以阻止为这些类发出vtable?我假设虚拟函数指针将传递给派生类,而虚拟析构函数将跳过这些类。有点像将这些类融为一体。
如果在C ++域中没有通用的通用语言,那么也许只适用于clang,gcc和vc?
#include<iostream>
template <typename D, typename B>
struct jelly : B
{
virtual void do_stuff() { static_cast<D*>(this)->D::do_some_other_stuff(); }
};
template <typename D>
struct jelly<D, void>
{
virtual void do_stuff() { static_cast<D*>(this)->D::do_some_other_stuff(); }
};
struct A : jelly<A, void>
{
void do_some_other_stuff() { std::cout << "A::do_some_other_stuff()\n"; }
};
struct B : jelly<B, A>
{
void do_some_other_stuff() { std::cout << "B::do_some_other_stuff()\n"; }
};
int main()
{
A a;
a.do_stuff(); // output: A::do_some_other_stuff()
B b;
b.do_stuff(); // output: B::do_some_other_stuff()
A& aa = b;
aa.do_stuff(); // output: B::do_some_other_stuff()
}
请澄清一下,这只是一个例子。它确实可以运行,但是jelly
代表的类数实际上是3个不同的类。一个由开发人员使用jelly
库显式继承,另外两个由隐式完成,然后再继承回开发人员自己的类。因为要增加3倍的班级数量,这让我很担心,这就是为什么我问这个问题。
答案 0 :(得分:1)
我唯一知道的执行此操作的编译器扩展是MSVC的__declspec(novtable)
:
__declspec
的这种形式可以应用于任何类声明,但是 应该只应用于纯接口类,即类 永远不会自己实例化。__declspec
停止 编译器从生成代码以初始化vfptr 类的构造函数和析构函数。在很多情况下 删除与vtable相关的对vtable的唯一引用 类,因此链接器将其删除。使用这种形式__declspec
可以大大减少代码大小。如果尝试实例化标记为
novtable
的类然后访问类成员,则会收到访问冲突(AV)。
当您使用MSVC的__interface
关键字时,暗示此修饰符。
答案 1 :(得分:0)
您这样做:
template <typename D, typename B>
struct jelly : B
{
virtual void do_stuff() { D::do_some_other_stuff(); }
};
然后您这样做:
struct B : jelly<B, A>
{
void do_some_other_stuff() { std::cout << "B::do_some_other_stuff()\n"; }
};
这意味着struct B
取决于jelly<B, A>
。那又需要调用B
(又名D
)函数do_some_other_stuff
,但是B
尚未定义。也许您需要将void do_stuff
移到类声明之外。无论如何,当您声明virtual
时,它不应内联。
另外,您正在使用
struct A : jelly<A, void>
翻译为:
struct jelly : void
{
virtual void do_stuff() { A::do_some_other_stuff(); }
};
您希望如何从void
继承?
样式注释:请勿仅使用struct
来避免public
。
当应该有一个typename D
(因为您是继承的)而不是一个class
时,请不要使用typename
。
不要使用名称A,B,C,D,而是使用一些更具描述性的名称。结构jelly : B
是从typename B
还是从struct B
继承的?
答案 2 :(得分:0)
如果您将成员函数声明为virtual
,则该类必须具有实现实现所需的任何机制,以实现C ++要求virtual
函数所做的工作。但这还表示类型现在是多态的,这要求类型能够执行C ++要求的多态类型可以做到的事情。具体来说,typeid
和dynamic_cast
。
那很重要。从多态类型派生的类本身就是多态的,无论它是否覆盖任何virtual
函数。这意味着您必须能够从该类的实例获取类型信息。是否实际这样做无关紧要;可以,因此必须存在允许它的机械。
对于vtable实现,这通常意味着每个多态类型都需要一个唯一 vtable对象。除了指向虚拟函数的指针之外,vtable还具有指向某些类型特定信息的索引或指针。由于vtable往往很小,因此围绕另一个vtable并不是特别麻烦。实际上,类型标识信息本身通常比vtable更重要。
现在,编译器提供了一些选项,使您可以删除运行时类型标识的所有痕迹。具体来说,typeid
不再起作用,并且dynamic_cast
再也不会抛出,因此不需要验证强制类型转换,编译器不再需要为A
提供与{{ 1}}。但是,the main target of the feature is the table of type_info
objects and other identifying information。因此,我无法谈谈此功能对vtable生成的影响。
但是,最终您无能为力。这些类可能会获取vtable,就是这样。