当生成类模板的虚拟方法的代码时,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中使用特化 导致隐式实例化发生或成员是虚拟成员函数的方式, 该计划格式错误,无需诊断。永远不会为显式生成隐式实例化 声明但未定义的专业化
答案 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,它们都会延迟实例化,直到翻译单元结束,除非被强制不执行),但我认为你的代码严格不符合。