2

I read this answer by Howard Hinnant (Is std::unique_ptr<T> required to know the full definition of T?) and then this answer (How is a template instantiated?) and I was just thinking. If you have a class like so

class Something {
    Something(); 
    ~Something();
    class Impl;
    std::unique_ptr<Impl> impl;
};

The unique_ptr will be instantiated at that point when the class is compiled (as I could make out from the other answer above). Then why is it okay to not have the class Impl defined later on? Won't the instantiation require the destructor of Impl to be present?

Note The following is in an effort to clarify what I am asking above.

The way I am thinking about it, when the compiler goes over the definition of the class Something. It will see the declaration of the nested class Impl and then it will see the declaration of the unique_ptr<Impl>, and at that point. It will instantiate the template unique_ptr with Impl. And that instantiated code will contain a call to the destructor of Impl. Since at this point we have code that includes a call to the destructor of an incomplete class, how is the code above safe?

Community
  • 1
  • 1
Curious
  • 20,870
  • 8
  • 61
  • 146

2 Answers2

1

The accepted answer to the first question contains a table of use cases where the complete definition of Impl is required.

In your case, the compiler implicitly generates the following member functions:

  • Copy constructor
  • Move constructor
  • Copy assignment operator
  • Move assignment operator.

All of them require the complete definition of Impl.

If you explicitly declare those functions and define them where the complete definition of Impl is available, you'll be OK.

Update

It will see the declaration of the nested class Impl and then it will see the declaration of the unique_ptr<Impl>, and at that point. It will instantiate the template unique_ptr with Impl.

That is correct only to an extent. Not all member functions of unique_ptr will be instantiated at that time.

And that instantiated code will contain a call to the destructor of Impl.

Not correct. The compiler will generate the code (or instantiate) the destructor of std::unique_ptr<Impl> only when it is needed. That place is the destructor of Something.

Destructor of Something will need destructor of std::unique_ptr<Impl>.
Destructor of std::unique_ptr<Impl> needs the complete definition of Impl.

In other words, the complete definition of Impl must be visible to the destructor of Something.

PS

More on template instantiation can be found at Template instantiation details of GCC and MS compilers.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Member functions for which class? Also I saw the accepted answer to the first question but still did not understand, the complete `unique_ptr` class will be instantiated with the given type at that point in code (the declaration of the pointer to impl) and with that instantiation will be the code that calls the destructor of `Impl` , but that is not yet visible. Then how does the code compile? – Curious Apr 13 '17 at 03:36
  • @Curious, in the linked answer, `P` is either `shared_ptr` or `unique_ptr`. In your case, the implicitly generated copy constructor will invoke the copy constructor of `unique_ptr`. Extend that logic to the other compiler generated member functions of your class. – R Sahu Apr 13 '17 at 03:40
  • I don't think I am conveying my thoughts properly, I'll try and clarify in the question – Curious Apr 13 '17 at 03:41
  • Posted the clarification! – Curious Apr 13 '17 at 03:44
  • @Curious, I added an Update that responds to your concerns. – R Sahu Apr 13 '17 at 03:58
  • Thanks! Could you link me to something that explains how template instantiation works? I feel like I am missing some things there. Like I don't really know which functions are instantiated when. – Curious Apr 13 '17 at 04:00
  • @Curious, I added a link to a relevant question on SO. – R Sahu Apr 13 '17 at 04:06
  • Great thanks! One last thing, from the title of the question that is just explaining the template instantiation for GCC and Microsoft compilers. Does that mean that different compilers have different side effects when it comes to template instantiation? And if so does that also imply that the same code might work on one compiler and not another when it comes to things like mentioned in my question? – Curious Apr 13 '17 at 04:08
  • 1
    @Curious, I haven't looked up the standard but from the accepted answer to that question: *Whether a compiler actually implements two-phase lookup is not dictated by the standard. To be conformant, however, it should work as if it did*. – R Sahu Apr 13 '17 at 04:11
0

If you write try to compile the above code and create an object of Something, it will give an error message :

Semantic issue:
memory:2523:27: Invalid application of 'sizeof' to an incomplete type 
'Something::Impl'

In short the code is uncompilable and there is no point of taking about safety in this case.

Abdus Khazi
  • 453
  • 3
  • 17