简体   繁体   中英

CRTP and Incomplete Types

I'd like a short clarification on how complete types relate to CRTP. I thought this question was somewhat related. However, my question here question pertains to CRTP where a derived class member function explicitly calls the base class member function, which in turn calls a derived function. This appears different from calling a base class function on a derived type once within the main routine.

I have also read this question where it was explained that the static constexpr members of the base class that make use of the derived class are not initialized until the derived class is seen by the compiler and is complete. However in that case, the derived class was also templated.

Here is my question. Consider

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 {}
};

As I understand, each D remains an incomplete type until the closing brace of the class. I also understand that member function templates are not instantiated until they are used. Thus, if we ignore D::bar for now, it makes sense that the following is valid in main:

D d; d.foo(); 

What I need further clarification about is what constitutes use? For example, in defining D::bar , there is a call to B::foo (so that function is presumably instantiated there) which would then require D to be a complete type. But at the point where D::bar is being defined, D is not complete. Or is it?

I thought perhaps what could be happening is that where D::bar is being defined and calls B::foo , it forces the compiler to make a declaration for B::foo (no definition required). And perhaps the definition does not happen until D::bar is actually called at some other point. But I ran this at C++ Insights and became more confused as the explicit specialization of B to B<D> happens even before D is declared. Clarification would be appreciated!

Using B<D> as base class for D requires B<D> to be a complete class. Hence it will cause implicit instantiation of B<D> .

The point of instantiation of the class specialization is immediately before the namespace scope declaration requiring it, meaning before the definition of D . (by [temp.point]/4 )

The implicit instantiation of B<D> does not cause implicit instantiation of the member function definition for foo . Hence D won't be required to be complete here.


The definition

void bar() const { foo(); }

has an ODR-use of foo . Therefore it will cause implicit instantiation of B<D>::foo .

The points of instantiation for a member function specialization are immediately after the namespace scope declaration and at the end of the translation unit. (by [temp.point]/1 and [temp.point]/7.1 )

Both of these are after the definition of D and therefore D will be complete at these points. Consequently static_cast<const D*>(this)->baz(); is not a problem.


Note that D is not a template. The template instantiation rules do not apply to it. It doesn't matter whether bar is used or referred to at all. Neither does it matter whether you use D d; d.foo(); D d; d.foo(); or anything at all following the definition of D .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM