"Incomplete Type" in Class Which Has a Member of the Same Type of the Class Itself

Incomplete type in class which has a member of the same type of the class itself

At the time you declare your member, you are still defining the A class, so the type A is still undefined.

However, when you write A*, the compiler already knows that A stands for a class name, and so the type "pointer to A" is defined. That's why you can embed a pointer to the type your are defining.

The same logic applies also for other types, so if you just write:

class Foo;

You declare the class Foo, but you never define it. You can write:

Foo* foo;

But not:

Foo foo;

On another hand, what memory structure would you expect for your type A if the compiler allowed a recursive definition ?

However, its sometimes logically valid to have a type that somehow refer to another instance of the same type. People usually use pointers for that or even better: smart pointers (like boost::shared_ptr) to avoid having to deal with manual deletion.

Something like:

class A
{
private:
boost::shared_ptr<A> member;
};

C++ incomplete type class

Python has duck typing and doesn't actually create the member object before your call to its constructor.

In Python your variable can even contain a string and then later a class object. I don't see how you would have 'the same code'. You don't specify the type of your variables and they can contain whatever you want, only raising errors when wrongly manipulated.

In C++ the member A will be created at the same time as the class, leading to an infinite recursion.

Use a pointer if you want to choose when to allocate the member.

C++ - Incomplete class type?

If you put an instance of a class inside itself, if you notice, you are creating recursion, since every instance has its own Expression power, and this goes on forever. If you have a pointer though, you can control this infinite recursion, since at any time you can set power to nullptr, (or NULL/0, pre C++11), to end the recursion.

Why is my class an incomplete type inside itself?

The compiler parses code from top to bottom. When the compiler encounters the declaration of t, it hasn't yet seen the end of the declaration of test, hence why test is an "incomplete type" at that location. The compiler doesn't know yet if there are any more data members following t, so it doesn't know how much space to reserve for t within each instance of test.

Basically, a class (or struct) cannot include a non-static instance of itself. Doing so would lead to a recursive declaration that never ends until the compiler fails.

However, a class/struct can include a pointer or reference to itself, since a pointer/reference has a fixed size at compile-time. For example, this is how linked-lists are implemented.

When does an Incomplete Type error occur in C++

This happens usually when the compiler has seen a forward declaration but no full definition of this type, while the type is being used somewhere. For example:

class A;

class B { A a; };

The second line will cause a compiler error and, depending on the compiler, will report an incomplete type (other compilers give you a different error, but the meaning is the same).

When you however just use a pointer to such a forward declaration no complain will come up, since the size of a pointer to a class is always known. Like this:

class A;
class B {
A *a;
std::shared_ptr<A> aPtr;
};

If you ask what could be wrong in a concrete application or library when this error comes up: that happens usually when a header is included which contains the forward declaration, but full definition hasn't been found yet. The solution is quite obvious: include also the header that gives you access to the full type. Sometimes you may also simply have no or the wrong namespace used for a type and need to correct that instead.

Why is it allowed to have a class that has an incomplete type static member of itself?

Conflating a couple of dissimilar issues here

static B test2; //error

fails because B's constructor requires arguments that haven't been provided and is private so it can't be accessed outside the class. Correct those and B is good to go.

class B {
public: // added for outside access to constructor
B(int a) {
printf("%d\n", a);
}
};

static B test2{1}; // added arguments

As for A, a static member is not part of a class instance, so the compiler doesn't need it to be complete. It doesn't take up any space, and at this point it's not being used for anything. It's just a declaration. You can declare all sorts of stuff without the compiler getting upset (so long as the declaration's syntax is good). You run into trouble when you try to use something without a definition.

Example:

void testfunc(const A &)
{

}

int main(){
testfunc(A::test);
}

fails at the linker because A::test was never defined. If you try to correct that with

class A {

public:

static A test{1}; // let's try to define it inline!
int a;
int b;
int c;

A(int d, int e) : a(d), b(e), c(15) {

}

A(int d) :A(d, 10) {
printf("%d %d %d", a, b, c);
}
};

now the compiler cares enough to report that the class is incomplete. We'll have to move the definition outside the class where it is complete

class A {

public:

static A test; //declared
int a;
int b;
int c;

A(int d, int e) : a(d), b(e), c(15) {

}

A(int d) :A(d, 10) {
printf("%d %d %d", a, b, c);
}
};
A A::test{1}; // defined

Now

void testfunc(const A &)
{

}

int main(){
testfunc(A::test);
}

will compile and link.

Documentation on the care and feeding of static members.

C++ error : field has incomplete type - declaring it's own class object

The class isn't fully defined until that closing brace. Before that you can't define objects of the class. A major reason is that the size of the object isn't known yet, so the compiler doesn't know how much memory to allocate for the member variables.

When you use pointers or references to the class, the compiler will know how much memory a pointer or reference takes up, as it's unrelated to the size of the actual class.

How am I able to use an incomplete class as an argument in itself?

The class struct test is not an incomplete type.

From cppreference.com:

Incomplete type

  • the type void (possibly cv-qualified);
  • incompletely-defined object types
    • class type that has been declared (e.g. by forward declaration) but not defined;
    • array of unknown bound;
    • array of elements of incomplete type;
    • enumeration type from the point of declaration until its underlying type is determined.

All other types are complete.

Test it out yourself, put a test into your call function, like:

void call(test t1)
{
std::cout << sizeof t1 << std::endl;
}

Field has incomplete type question for classes that reference themselves

Your underlying problem is that you assume C++ works like Java. And you are making that assumption in a context where C++ is emphatically different from Java.

The "incomplete type" error is because of

TreeNode children [];

since declaring an array member of a class without a dimension gives an incomplete type (an array with unknown dimension is incomplete).

If you were to supply an array dimension that is known at compile time.

TreeNode children [25];

that would be a complete type, but is still invalid within class TreeNode since it effectively means that a TreeNode object contains 25 instances of TreeNode, each of which contains 25 instances of TreeNode - which is infinitely recursive containment.

Unlike Java, which uses reference semantics by default, C++ uses value semantics. A consequence of this is that an object of type TreeNode cannot contain an instance of TreeNode (i.e. another complete object of type TreeNode) and cannot contain an array of TreeNode (i.e. a complete SET of objects of type TreeNode).

The way to handle that is by indirection - explicitly using a code construct that allows use of pointer or reference semantics. One example is

#include <vector>
class TreeNode
{
public:
int value;
std:vector<TreeNode> children;
TreeNode(int x, TreeNode kids[], std::size_t n);
virtual ~TreeNode();
};

since a std::vector<TreeNode> actually (in effect) contains a pointer to a dynamically allocated array of TreeNode, and manages dynamic allocation and deallocation of TreeNode objects at run time.

The constructor declaration in the above

  TreeNode(int x, TreeNode kids[], std::size_t n);

works a little differently than you expect. I am adding third argument, n, to represent the number of kids being passed since there is no way in standard C++ to obtain the number of elements of an array argument. The second argument can use array syntax, but this is actually treated by the compiler as a pointer. So the above constructor declaration is actually equivalent to

  TreeNode(int x, TreeNode *kids, std::size_t n);

Either way, this constructor needs to be defined. One way would be

TreeNode::TreeNode(int x, TreeNode *kids, std::size_t n) : children(n)
{
// presumably also do something with x

for (std::size_t i = 0; i < n; ++i)
children[i] = kids[i];
}

where the children(n) in the constructor initialiser list has the effect, for a std::vector, of setting the number of elements to n.

An even better way to implement the constructoror would be to use the fact that a std::vector can be initialised using a pair of iterators

TreeNode::TreeNode(int x, TreeNode *kids, std::size_t n) : children(kids, kids + n)
{
// presumably also do something with x
}

where children(kids, kids + n) uses pointer syntax. kids is equivalent to &kids[0] (i.e. the address of the first element of a passed array) and kids + n is equivalent to &kids[n] (i.e. the address of the "one past the end" element of kids).



Related Topics



Leave a reply



Submit