什么时候是类模板实例化的虚方法?

时间:2013-09-19 15:37:37

标签: c++ templates c++11 g++

当生成类模板的虚拟方法的代码时,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&lt; 0&gt; :: f()的任何显式特化之前隐式地实例化类模板A.在上面的示例中,专门化是在类模板A的隐式实例化发生之后完成的。现在,至少我的ARM-Compiler和g ++选择A&lt; 0&gt; :: f()的专用版本,i。即main()程序将“从A&lt; 0&gt; :: f()输出”打印到屏幕上。

我是否可以始终确定,在隐式实例化此类模板后,定义类模板的虚方法的特化是否足够?如果观察到的行为得到C ++标准的支持,我会感觉更好。我没有找到关于这个主题的任何明确声明。最接近的部分是14.7.3 / 6,这在虚拟方法方面有点不明确:

  

如果模板,成员模板或类模板的成员明确专门化,那么   专业化应在首次使用该专业化之前宣布,该专业化将导致隐含的实例   在发生此类使用的每个翻译单位中进行;无需诊断。如果   程序不提供显式特化的定义,并且在a中使用特化   导致隐式实例化发生或成员是虚拟成员函数的方式,   该计划格式错误,无需诊断。永远不会为显式生成隐式实例化   声明但未定义的专业化

2 个答案:

答案 0 :(得分:2)

我们非常肯定这是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 - 如果是这样,它能够在TU中稍后修复该vtable的新声明成员。

答案 1 :(得分:0)

该标准尚不清楚。隐式实例化的相关部分是14.7.1p2:

  

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

“要求定义存在”不幸是一个完全未定义的术语,但我认为可以做出一个好的论据,即“使用过的”至少是其中的一个子集。对于“odr-used”,3.2p2中的大文字说:

  

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

换句话说,虚拟成员是他们纯粹存在的必要条件(没有双关语)。

所以我认为可以建立一个参数,至少允许编译器在实例化包含类时尝试实例化所有虚函数。我不知道有任何编译器这样做(AFAIK,它们都会延迟实例化,直到翻译单元结束,除非被强制不执行),但我认为你的代码严格不符合。