4
#include <memory>
class Data;
std::unique_ptr<Data> p;
//class Data{}; // not working without this
int main(){}

Compilation of this code with g++-5 gives such error:

invalid application of ‘sizeof’ to incomplete type ‘Data’

Can somebody explain why if I uncomment 4th line compilation will be successful? As I understand in 3rd line compiler hasn't full information about type Data. We have only forward declaration in this line. Real declaration occures in 4th line.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
poljak181
  • 585
  • 4
  • 12
  • 2
    [is-stdunique-ptr-required-to-know-the-full-definition-of-T](https://stackoverflow.com/a/6089065/2684539) and original source https://howardhinnant.github.io/incomplete.html – Jarod42 May 11 '18 at 17:06

2 Answers2

5

Target type of unique_ptr type may be incomplete at the point of template instantiation, but must be complete at the point when unique_ptr may try to dispose of the stored pointer because it is a requirement of default_delete that will be invoked. If you are using custom deleter then target type may be still incomplete.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • 2
    In other words, in OP's code it's the instantination of the destructor of `unique_ptr` (that seems to happen at the end of the translation unit since it's a global variable) that requires `Data` to be complete. – HolyBlackCat May 11 '18 at 17:08
  • 1
    *"but must be complete at the point"* in the TU in fact, as [the-point-of-instantiation-can-be-delayed-until-the-end-of-the-translation-unit](https://stackoverflow.com/questions/23030403/can-the-point-of-instantiation-be-delayed-until-the-end-of-the-translation-unit). – Jarod42 May 11 '18 at 17:44
  • 1
    OK, so you say it must be complete at the point where `unique_ptr`'s destructor is called. But in [this other example](https://pastebin.mozilla.org/9085397) the destructor would be called at the end of `main`. At this point `class Data` is still incomplete, but this does compile successfully with g++ 5.4.1. Does this then simply result in undefined behavior? – Ruslan May 14 '18 at 15:21
  • 1
    @Ruslan As mentioned in Jarod42's comment point of instantiation may be delayed to the end of translation unit. So the code in your example may be either valid or cause UB. – user7860670 May 14 '18 at 15:51
2

Your guess is correct. At the 3rd line compiler only knows that this type exists. You forward-declared it. unique_ptr is defined in such a way to give semantics similar to the regular pointer. In 2nd line you promise compiler to actually define the class when it's really needed, and compiler indeed needs the definition of Data class to call destructor on Data in p.

The sizeof error is a common way to "force" complete type definition at the certain point (in this case to prevent UB from calling delete on the incomplete type in p destructor).

Dan M.
  • 3,818
  • 1
  • 23
  • 41