[英]CRTP and Incomplete Types
我想简要说明一下完整类型与 CRTP 的关系。 我认为这个问题有点相关。 但是,我的问题与 CRTP 有关,其中派生类成员函数显式调用基类成员函数,而基类成员函数又调用派生函数。 这似乎与在主例程中调用派生类型的基类函数不同。
我还阅读了这个问题,其中解释了使用派生类的基类的static constexpr
成员在编译器看到派生类并完成之前不会初始化。 但是在这种情况下,派生类也被模板化了。
这是我的问题。 考虑
template<typename D> struct B{
void foo() const { static_cast<const D*>(this)->baz(); }
};
struct D : B<D> {
void bar() const { foo(); }
void baz() const {}
};
据我了解,在类的右大括号之前,每个D
仍然是不完整的类型。 我也知道成员函数模板在使用之前不会被实例化。 因此,如果我们暂时忽略D::bar
,则以下内容在 main 中有效是有道理的:
D d; d.foo();
我需要进一步澄清的是什么是使用? 例如,在定义D::bar
时,有一个对B::foo
的调用(因此该函数可能在那里被实例化),这将要求D
是一个完整的类型。 但是在定义D::bar
时, D
并不完整。 或者是吗?
我想也许可能发生的是,在定义D::bar
并调用B::foo
的地方,它会强制编译器为B::foo
进行声明(不需要定义)。 也许直到D::bar
在其他某个时间点实际调用时才发生定义。 但是我在C++ Insights上运行了这个并且变得更加困惑,因为B
到B<D>
的显式特化甚至在D
被声明之前就发生了。 澄清将不胜感激!
使用B<D>
作为D
的基类要求B<D>
是一个完整的类。 因此,它将导致B<D>
的隐式实例化。
类特化的实例化点就在需要它的命名空间范围声明之前,即在D
的定义之前。 (通过[temp.point]/4 )
B<D>
的隐式实例化不会导致foo
的成员函数定义的隐式实例化。 因此,这里不需要完成D
定义
void bar() const { foo(); }
有一个 ODR 使用foo
。 因此它将导致B<D>::foo
的隐式实例化。
成员函数特化的实例化点位于命名空间范围声明之后和翻译单元的末尾。 (通过[temp.point]/1和[temp.point]/7.1 )
这两个都在D
的定义之后,因此D
将在这些点上完成。 因此static_cast<const D*>(this)->baz();
不是问题。
请注意, D
不是模板。 模板实例化规则不适用于它。 使用或引用bar
并不重要。 是否使用D d; d.foo();
D d; d.foo();
或任何遵循D
定义的东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.