Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRTP -- accessing incomplete type members

Related questions: one, two

After trying to understand CRTP for several days it seems that now I understand even less than before:)

Consider the following code:

01 #include <iostream>
02 
03 template <class IMPL>
04 class Interace
05 {
06 public:
07     typedef typename IMPL::TYPE TYPE;  // ERROR: "...invalid use of incomplete type..."
08     void foo() { IMPL::impl(); }       // then why does this work?
09 };
10 
11 class Implementation : public Interface<Implementation>
12 {
13 public:
14    typedef int TYPE;
15    static void impl() { std::cout << "impl() " << std::endl; }
16 };
17 
18 
19 int main()
20 {
21     Implementation obj;
22     obj.foo();
23 }

The questions are:

  1. Why I can call function from IMPL:: (line 8) but cannot access type fileds (line 7)? In related question it is said that IMPL is an incomplete type at this point. But why then line 8 is correct?

  2. What it the order of type declaration/definition? As I see it:

    a. Interface template -- OK. Doesn't bring any problems until instantiating

    b. line 11 -- after class Implementation -- Implementation type declared but not defined.

    c. line 11 -- after Interface<Implementation> -- template instantiation. At this point Implementation is already known (but not defined!) due to step (b). Compiler "injects" code with IMPL replaced with Implementation. Here, to my point of view, neither line 7, neither line 8 are not legal because at this point, compiler doesn't know that Implementation has these members. How does it knows than?

Or maybe Instantiation really goes at line 21? But in that case why line 07 doesn't work?

More I think about it, less understanding of C++ type fundamentals I have. Any clarification is appreciated.

like image 869
DimG Avatar asked Feb 16 '16 09:02

DimG


1 Answers

When a class template is instantiated, its members other than non-virtual member functions are instantiated together with it. Non-virtual member functions, however, are only instantiated when odr-used (basically, called or have their address taken).

When the compiler encounters class Implementation : public Interface<Implementation>, it needs to instantiate Interface<Implementation>. At this point, Implementation is still an incomplete type, its TYPE member has not yet been seen. On the other hand, Interface<Implementation>::foo is only instantiated later, when it's called in main. At that point, Implementation is a complete type.

like image 197
Igor Tandetnik Avatar answered Oct 02 '22 06:10

Igor Tandetnik