2

Consider the following code as C++ Primer 5th Ed. section 7.6.

class Bar {
public:
    // ...
private:
    static Bar mem1; // ok: static member can have incomplete type --> Tag 1
    Bar *mem2;       // ok: pointer member can have incomplete type --> Tag 2
    Bar mem3;        // error: data members must have complete type --> Tag 3
    int x=4; --> Tag 4
}
  1. The above class definition considered to be a definition. This so call definition has NO STORAGE allocated like a created object.
  2. Tag 1 & 2 works because Bar are considered declaration. I am good with that since there's no object of class Bar created. These are declaration within the class definition.
  3. Tag 3 is where I have trouble with. To me tag 3 is considered declaration because no instance of class Bar is created yet.
  4. Tag 4 - I will also considered this a declaration at this stage since no instance of class Bar is created.

Point is those data members like tag 3 & 4, I would considered them as definition only if instance of class Bar is created outside of the class definition.

Bar bar; --> this would then initialize tag 3 & 4 --> Tag 5

Why is tag 3 needs to be complete as at this line there is no storage allocated since it's still a declaration? Tag 3 is not an instance yet at this line. Tag 3 only becomes a definition and have storage allocated when we create the class Bar outside of the class definition as in Tag 5.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
yapkm01
  • 3,590
  • 7
  • 37
  • 62
  • Tag 3 needs to be complete because the compiler needs to know its size, so as to know how to allocate a `Bar`. – user207421 Sep 18 '21 at 04:13
  • Why? No instance of class Bar is created yet. So why does it need the size as in Tag 3? Remember no object of class Bar is created at that line yet. Object of class Bar is only created outside of class Bar definition as shown by Tag 5 – yapkm01 Sep 18 '21 at 04:14
  • Exact same reason as global variables declared in a header file. Static class members, like global variables, ultimately need a definition in a compilation unit (.cpp file) to have an address that can be linked to. – selbie Sep 18 '21 at 04:18
  • 1
    @yapkm01 `Bar mem3;` is just shorthand for writing `Bar mem3 = Bar();`. You can see that by looking [here](https://godbolt.org/z/EexveMe5a). (The default constructor of the class `Bar` gets called, but in your case, since `Bar` is still not yet defined, the error pops up.) – Ruks Sep 18 '21 at 04:18
  • @Ruks That's my point. Why is an object of Bar created for data member mem3 when there is no object of class Bar created yet? Same question. Why is Tag 4 storage allocated at that line when no object of class Bar created yet? Only when Tag 5 happens then its data member of that class Bar gets created. What's happening is like creating a baby but the mother doesn't even exists. – yapkm01 Sep 18 '21 at 04:23
  • It's not that an object has been created, but that no object of that type could ever be created (what would its size be?). This is considered to be invalid, and so the compiler doesn't allow it. – cigien Sep 18 '21 at 04:27
  • @cigien Sry. Don't get it .. I presume you're referring to Tag 3 but i am still not clear – yapkm01 Sep 18 '21 at 04:28
  • Yes. An object that contains itself would have infinite size, because it would contain an object of itself, which would contain an object of itself, and so on ... I'm not really sure how to explain it very differently. There are a bunch of linked questions on the duplicate target that explain it in different words, maybe that will help. – cigien Sep 18 '21 at 04:34
  • Use tag 4 for discussion. Storage allocated. Why? No class of Class Bar is even created (assume Tag 5 is not there). – yapkm01 Sep 18 '21 at 04:38
  • Please don't close my questions as it has not been answered. Appreciate that. – yapkm01 Sep 18 '21 at 04:44
  • You should ping the close voter, assuming you're asking them to reopen the question. In this case, I think your question is the same as the linked one. There's a reopen vote on the question, so it will get reviewed, and if other users agree that it's not a duplicate, they'll vote to reopen as well. – cigien Sep 18 '21 at 05:53
  • Also, I'm not sure what you mean by "storage is allocated" for tag 4. No storage needs to be allocated until an object is created, but when an object is created, storage for an int is allocated. When an object of `Bar` is created, storage for a `Bar` would need to be allocated, but as mentioned above, that's impossible. – cigien Sep 18 '21 at 05:55
  • 1
    The language simply requires that the size of a type be well defined. If a type contains an instance of itself, its size is infinite, and the compiler stops you from doing that, *even before* an object of that type is created. May I ask why this is a concern? Do you have any particular reason to *want* the type to be accepted by the compiler? – cigien Sep 18 '21 at 17:54
  • No. I am still studying C++ and sometimes having difficulty since i am from the Java & C background. Moving forward studying C++, i find that the language itself is humongous and complex. No wonder lots of people prefer to go with other languages. Thanks a lot. – yapkm01 Sep 18 '21 at 18:24

1 Answers1

1

Remember c++ class definitions define a memory layout. Unlike most higher level language there is no reference implied in the declaration of a variable is expressed as a litteral block of memory for that data inline in the continuous block of memory for instances of that class or structure.

The declaration of an instance member variable of type Bar as a piece of Bar creates a unresolvible recursive definition because the memory of Bar contains as a piece all the memory of a Bar in addition to other stuff.

The static variable works because it does not have to be allocated in every instance of Bar just 1 for all instances of Bar to share and is more of a global variable associated with the namespace of the class and not a true member in the traditional sense.

The pointer works because a pointer simply refers to another piece of memory that should contain a Bar thus not running into the same recursive expansion problem when building the memory layout.

user1424589
  • 155
  • 4
  • What about Tag 4? Why there's storage allocated there? No instance of class Bar exists yet. (assume tag 5 is not there). It's like having a baby without the mother .. lol – yapkm01 Sep 18 '21 at 04:40
  • This makes the variable be initialized as that value in each instance. This is only allowed because this is a constant expression. The compiler will simply bake that initialization into every constructor so it does not really exist in a strict sense – user1424589 Sep 20 '21 at 03:25