"手卷"的优点是什么? vtable方法?

时间:2015-11-02 19:47:05

标签: c++ type-erasure vtable

最近,我遇到了几个使用"手动滚动"的类型擦除实现。 vtable - Adobe ASL's any_regular_t就是一个例子,虽然我已经看到它在Boost ASIO中使用过(对于完成例程队列)。

基本上,父类型传递一个指针,该指针指向一个完整的子类型中定义的函数指针的静态类型,类似于下面的...

struct parent_t;

struct vtbl {
  void (*invoke)(parent_t *, std::ostream &);
};

struct parent_t {
  vtbl *vt;
  parent_t(vtbl *v) : vt(v) { }
  void invoke(std::ostream &os) {
    vt->invoke(this, os);
  }
};

template<typename T>
struct child_t : parent_t {
  child_t(T val) : parent_t(&vt_), value_(val) { }
  void invoke(std::ostream &os) {
    // Actual implementation here
    ...
  }
private:
  static void invoke_impl(parent_t *p, std::ostream &os) {
    static_cast<child_t *>(p)->invoke(os);
  }
  T value_;
  static vtbl vt_;
};

template<typename T>
vtbl child_t<T>::vt_ = { &child_t::invoke_impl };

我的问题是,这个成语的优势是什么??据我所知,它只是重新实现编译器将免费提供的内容。 parent_t::invoke调用vtbl::invoke时,不会有额外间接的开销。

我猜测它可能与编译器能够内联或优化对vtbl::invoke或其他东西的调用有关,但我觉得不舒服足够的Assembler能够自己解决这个问题。

1 个答案:

答案 0 :(得分:8)

具有有用vtable的类基本上要求动态分配。虽然你可以做一个固定的存储缓​​冲区并在那里分配,但这很麻烦;一旦你去virtual,你就无法合理控制实例的大小。使用手动vtable,即可。

瞥了一眼有问题的来源,关于各种结构的大小有很多断言(因为在一种情况下它们需要适合两个双打的数组)。

也是一个&#34;班级&#34;手工制作的vtable可以是标准布局;如果你这样做,某些种类的铸造变得合法。我没有看到在Adobe代码中使用它。

在某些情况下,它可以完全与vtable分开分配(正如我在基于视图的类型擦除时所做的那样:我为传入类型创建自定义vtable,并为其存储void*,然后将我的界面发送到所说的自定义vtable)。我没有看到在Adobe代码中使用它;但是any_regular_view作为any_regular的伪引用可能会使用此技术。我将它用于can_construct<T>sink<T>function_view<Sig>甚至move_only_function<Sig>等所有权(所有权由unique_ptr处理,通过本地vtable进行操作1条)。

如果你有一个手工制作的vtable,你可以创建动态类,你可以在其中分配一个vtable条目并将其指针设置为你选择的任何东西(可能是以编程方式)。如果你有10个方法,每个方法可以处于10个状态之一,那么需要10 ^ 10个不同的类和普通的vtable。使用手工制作的vtable,您只需管理每个课程即可。在某个地方的桌子上的生命(所以实例不会比这个课程更长)。

作为一个例子,我可以采用一种方法,并在&#34;之前添加一个&#34;运行。或&#34;追赶&#34;方法,在类的特定实例(具有仔细的生命周期管理),或在该类的每个实例上。

结果vtable也可能以各种方式比编译器生成的vtable更简单,因为它们并不强大。例如,编译器生成的vtable处理虚拟继承和动态转换。除非使用,否则虚拟继承情况可能没有开销,但动态强制转换可能需要开销。

您还可以控制初始化。使用编译器生成的vtable,表的状态被定义(或未定义),如标准所示:使用手动滚动,您可以确保选择的任何不变量。

在C ++出现之前,OO模式存在于C语言中。 C ++只是选择了一些合理的选项;当你回到伪C风格的手册OO时,你可以访问这些替代选项。你可以装扮(用胶水),以便他们像普通的C ++类型一样看起来给临时用户,而在里面他们只是。