何时实例化类模板的 vitual 方法

When are vitual methods of a class template instantiated?

本文关键字:vitual 方法 实例化 何时      更新时间:2023-10-16

C++标准是否说明了生成类模板虚拟方法代码的确切时间点?

请考虑以下示例:

class Interface
{
  public:
    virtual void f() = 0;
};
template <unsigned int V>
class A : public Interface
{
  public:
    virtual void f()
    {
    }
};
Interface* instantiate()
{
  // class template instantiation with argument V=0
  return new A<0>();
}
// specialization of f() for template argument V=0
template <> void A<0>::f()
{
  cout << "Output from A<0>::f()" << endl;
};
int main() 
{
  Interface* i = instantiate();
  i->f();
  return 0;
}

类模板 A 声明了一个虚拟方法 f()。在我们的示例中,函数 instantiate() 在完成 A<0>::f() 的任何显式专用化之前隐式实例化类模板 A。在上面的示例中,专用化是在类模板 A 的隐式实例化发生后完成的。现在,至少我的ARM编译器和g++选择了A<0>::f()的专用版本,即main()程序将"A<0>::f()的输出"打印到屏幕上。

我是否总是可以确定,在类模板被隐式实例化之后,定义类模板的虚拟方法的专用化就足够了?如果观察到的行为得到C++标准的支持,我会感觉更好。我没有找到关于这个话题的任何明确声明。最接近的部分是 14.7.3/6,当涉及到虚拟方法时,这有点不具体:

如果模板、成员模板或类模板的成员显式专用,则 专业化应在首次使用该专业化之前声明,该专业化会导致隐含的 在发生这种使用的每个翻译单位中,进行这种使用;无需诊断。如果 程序不提供显式专业化的定义,并且该专业化用于 导致发生隐式实例化或成员是虚拟成员函数的方式, 程序格式不正确,无需诊断。永远不会为显式生成隐式实例化 已声明但未定义的专业化

我们很确定它是 UB。

在实践中:

new A<0>()

将生成对构造函数的调用,并且编译器需要它的定义才能可用。如果您尝试在此调用后专门化A<0>::A() gcc 将出错:

error: specialization of ‘A<V>::A() [with V = 0]’ after instantiation

构造函数将具有设置类的多态标头的代码,该标头将包含指向 vtable 的指针。 在该 vtable 中将是 Interface::f 的条目,但它在这一点上甚至没有声明最终将填充该插槽的符号,您的显式专业化A<0>::f - 所以它归结为实现质量问题 - 编译器是否在完成类类型的同时设计 vtable - 如果是这样,它是否能够在稍后的图。

标准对此

非常不清楚。关于隐式实例化的相关部分是 14.7.1p2:

[...]当在需要成员定义的上下文中引用该专用化时,成员的专用化是隐式实例化的;

不幸的是,"

需要定义存在"是一个完全未定义的术语,但我认为可以提出一个很好的论据,即"odr-used"至少是其中的一个子集。对于"odr-used",3.2p2 中的大文本墙说:

如果虚拟成员函数不是纯成员函数,则使用 odr。

换句话说,虚拟成员是由于其存在的(没有双关语)而必需的。

所以我认为可以提出一个论点,即编译器至少可以在实例化包含类的那一刻尝试实例化所有虚函数。我不知道有任何编译器这样做(AFAIK,除非被迫不这样做,否则它们都会延迟实例化直到翻译单元的末尾),但我认为您的代码严格不符合要求。