C++CRTP虚拟函数的实例化点

C++ CRTP virtual function point of instantiation

本文关键字:实例化 函数 虚拟 C++CRTP      更新时间:2023-10-16

我试图了解一个简单的CRTP模式是否符合标准。

下面的代码按预期编译并工作(在clang上)。

但我对相关标准章节/段落的理解是虚拟函数CRTP<派生的,Base>::DoSomething()应该在代码的(B)点,在那里Derived的完整声明不可用。因此,内部typedef Type也不应该可用。

有人能指出验证此代码的相关标准章节吗?

换句话说,在这种情况下,虚拟函数被实例化ATFER点C?提前感谢您的任何见解。

Francesco

//-------------------------
// START CODE
#include <iostream>
struct Type1 {};
struct Type2 {};
struct Base
{
  virtual ~Base() {}
  virtual void DoSomething() = 0;
};
template< typename T, typename U >
struct CRTP : U
{
  virtual void DoSomething() { DoSomething( typename T::Type() ); }
 void DoSomething( Type1 ) { std::cout << "1n"; }
 void DoSomething( Type2 ) { std::cout << "2n"; }
};
// (A) point of inst. of CRTP< Derived, Base > ( 14.7.1.4 ) ??
// (B) point of inst. of CRTP< Derived, Base >::DoSomething() (14.6.4.1.4 ) ??
struct Derived : CRTP< Derived, Base >
{
  typedef Type2 Type;
};
// (C)
int main()
{
  Base *  ptr = new Derived;
  ptr->DoSomething();
  delete ptr;
}
// END CODE
//-------------------------

相关(?)标准段落:

14.6.4.14如果一个虚拟函数是隐式实例化的,它的实例化点紧跟在它的封闭类模板专门化的实例化点之后

14.7.14如果在需要完全定义的对象类型的上下文中使用类类型,或者如果类类型的完整性可能影响程序的语义,则类模板专门化是隐式实例化的。

14.7.19实现不应隐式实例化不需要实例化的类模板的函数模板、成员模板、非虚拟成员函数、成员类或静态数据成员。如果不实例化类模板的虚拟成员函数,则未指定实现是否隐式实例化该虚拟成员函数。

这似乎是编译器将CRTP<Derived, Base>::DoSomething()的实例化延迟到翻译单元结束的结果,这是允许的(请参阅CWG问题993)。

CRTP<Derived, Base>肯定是在Derived定义之前实例化的(§14.6.4.1[温度点]/p4,所有引号都指向N3936):

对于类模板专门化,类成员模板专业化,或一个阶级成员的专业化模板,如果专门化是隐式实例化的,因为它从另一个模板专用化中引用,如果引用专门化的上下文取决于template参数,如果未实例化专门化在实例化封闭模板之前实例化紧接在封闭模板。否则,此类的实例化点specialization紧跟在命名空间作用域声明之前,或者指专业化的定义

CRTP<Derived, Base>::DoSomething()是否需要实例化取决于在需要成员定义存在的上下文中引用的短语的含义(§14.7.1[temp.inst]/p2)。所有非纯虚拟函数都是odr使用的(§3.2[basic.def.odr]/p2),"每个程序应包含该程序中使用的odr的每个非内联函数或变量的一个定义"(§3.2[basic.def.odr]/p4);这是否算作"在需要成员定义存在的上下文中引用"尚不清楚。

(然而,即使不需要实例化,编译器仍然可以根据§14.7.1[temp.inst]/p11-"如果不实例化虚拟成员函数,则未指定实现是否隐式实例化类模板的虚拟成员函数。")

如果CRTP<Derived, Base>::DoSomething()确实被实例化,则情况由§14.6.4.1[温度点]/p5和p8(重点矿):涵盖

5如果一个虚拟函数是隐式实例化的,那么它的点实例化紧跟在的实例化点之后它的封闭类模板专门化。

8函数模板、成员函数模板的专门化,成员函数的,或者类模板的静态数据成员可能在翻译单元内具有多个实例化点,以及除了上面描述的实例化点之外,对于任何在翻译单元,翻译单元的末尾也被认为是实例化点类模板的专业化位于翻译单元中的大多数实例化点。A.任何模板的专业化都可能在多个翻译单元如果实例化的两个不同点根据模板的不同赋予模板不同的含义定义规则(3.2),程序格式错误,无诊断必需

也就是说,它有两个实例化点,一个在CRTP< Derived, Base >的实例化点之后,另一个在翻译单元的末尾。在这种情况下,在实例化的两个点上,typename T::Type的名称查找将产生不同的结果,因此程序格式不正确,不需要诊断。

new Derived的使用会导致Derived类被实例化。

更正:Derived本身不是一个模板,因此需要立即使用其结构布局和包含的成员声明。这导致CRTP<Derived,Base>在Derived定义之后立即被实例化。等我有更多时间的时候,我得查一下正式的标准;但问题仍然是,CRTP的实例化只计算出结构和可用成员,而没有计算出成员函数的主体;并且当它这样做时,它知道派生类的结构和成员

成员函数在使用之前不会被实例化(这里是构造函数),并且在那个时候它已经拥有了类本身。另一件需要查找的事情是,由于Derived的构造函数不是模板,它是在类之后立即生成的,还是仅在需要时生成的。如果是前者,可以通过使Derived成为一个带有伪参数的模板来使其变得懒惰。但这并不影响这个特定的问题:无论是在Derived之后,还是在main之后,在解析Derived的声明之前,函数实例化仍然是而不是

这导致CRTP<Derived,Base>被实例化。但在这两种情况下,只需要类结构,而不需要任何成员的实际代码。删除所有的内联函数体,您将看到在这一点上没有问题。

现在使用了Derived默认构造函数,因此Derived::Derived()被隐式实例化。实例化的点紧跟着main的定义。

在实例化Derived::Derived()时,它需要CRTP<Derived,Base>::CRTP()。它在与需要它的模板实例化相同的点被实例化。该构造函数需要所有的虚拟函数,因此DoSomething()再次在与启动它的实例化相同的位置被实例化。你可以看到,所有这些都发生在完全呈现的派生类的完整定义已知之后,就所有成员(而非职能机构)的所有声明而言。

这是缺失的见解:类定义不包括成员函数定义,即使它们是在类定义的词法封闭区域内给出的。请记住定义声明之间的区别,分别针对类和函数。