I'm getting an error "invalid use of incomplete type 'class map'
Categories:
Resolving 'invalid use of incomplete type' in C++

Understand and fix the common C++ error 'invalid use of incomplete type' by mastering forward declarations, header inclusion, and proper class definitions.
The C++ compiler error "invalid use of incomplete type 'class map'" (or any other class name) is a common hurdle for developers, especially when dealing with complex class relationships and header files. This error typically arises when you try to use a class in a way that requires its full definition, but the compiler only has a forward declaration of it. This article will demystify incomplete types, explain why this error occurs, and provide practical solutions to resolve it.
Understanding Incomplete Types
An incomplete type is a type that has been declared but not yet defined. For a class, this means the compiler knows the class exists (e.g., class MyClass;
), but it doesn't know its members (data or functions), its size, or its layout in memory. This information is crucial for certain operations.
Forward declarations are powerful tools for breaking circular dependencies between headers and reducing compilation times. However, they come with limitations. You can declare a pointer or a reference to an incomplete type, as the compiler only needs to know the size of a pointer/reference, not the full class definition. But you cannot perform operations that require knowing the class's size or its members, such as:
- Creating an instance of the class (e.g.,
MyClass obj;
) - Accessing its members (e.g.,
obj.member;
) - Calling its methods (e.g.,
obj.doSomething();
) - Inheriting from it
- Using it as a base class for a member variable (e.g.,
MyClass member;
) - Using it as a template argument where the template requires a complete type.
classDiagram class Compiler { +KnowsDeclaration() +NeedsDefinition() } class ForwardDeclaration { +class MyClass; } class FullDefinition { +class MyClass { -int data; +void method(); } } class Operations { +DeclarePointer() +DeclareReference() -CreateInstance() -AccessMember() -CallMethod() } ForwardDeclaration --|> Compiler : Provides 'exists' info FullDefinition --|> Compiler : Provides 'details' info Compiler --> Operations : Enables/Disables based on info Operations ..> ForwardDeclaration : Allowed (pointers/references) Operations ..> FullDefinition : Required (instances/members)
Compiler's understanding of forward declarations vs. full definitions
Common Scenarios and Solutions
The "invalid use of incomplete type" error often points to a missing #include
directive or an incorrect use of forward declarations. Let's explore the most common scenarios and their fixes.
Scenario 1: Missing Header Inclusion
This is the most frequent cause. You've forward-declared a class (e.g., class MyClass;
) in a header file, but in a .cpp
file or another header where you actually use the class (e.g., creating an object, calling a method), you forgot to include the header that contains the full definition of MyClass
.
Example:
my_class.h
:
#ifndef MY_CLASS_H
#define MY_CLASS_H
class MyClass {
public:
void doSomething();
};
#endif // MY_CLASS_H
another_class.h
:
#ifndef ANOTHER_CLASS_H
#define ANOTHER_CLASS_H
class MyClass; // Forward declaration
class AnotherClass {
public:
void process(MyClass* obj);
// MyClass member; // ERROR: Incomplete type
};
#endif // ANOTHER_CLASS_H
another_class.cpp
:
#include "another_class.h"
// Missing #include "my_class.h" here!
void AnotherClass::process(MyClass* obj) {
obj->doSomething(); // ERROR: 'doSomething' is not a member of 'MyClass'
// because MyClass is incomplete here.
}
Solution: Always include the full definition header (#include "my_class.h"
) in the .cpp
file where you implement methods that use the class's members or create instances. If you need to create an instance or access members in a header file, you must include the full definition header there as well, but be cautious about creating circular dependencies.
// Corrected another_class.cpp
#include "another_class.h"
#include "my_class.h" // <--- Added this line
void AnotherClass::process(MyClass* obj) {
obj->doSomething(); // Now compiles correctly
}
Corrected implementation with full header inclusion
Scenario 2: Using an Incomplete Type as a Member Variable
You cannot declare a member variable of an incomplete type directly, because the compiler needs to know the size of the class to allocate memory for the containing object.
Example:
container.h
:
#ifndef CONTAINER_H
#define CONTAINER_H
class ContainedClass; // Forward declaration
class Container {
private:
ContainedClass containedObject; // ERROR: 'containedObject' has incomplete type
public:
void useContained();
};
#endif // CONTAINER_H
Solution: If you need to store an object of ContainedClass
within Container
, you must include the full definition of ContainedClass
in container.h
. If you want to avoid including the full definition in the header (e.g., to break a circular dependency or reduce compilation time), you must store a pointer or a reference to ContainedClass
instead. This often involves using smart pointers like std::unique_ptr
or std::shared_ptr
for proper memory management.
Using a pointer/reference allows Container
to know only that ContainedClass
exists, not its size or members, thus keeping container.h
lightweight.
// Corrected container.h using a smart pointer
#ifndef CONTAINER_H
#define CONTAINER_H
#include <memory> // For std::unique_ptr
class ContainedClass; // Forward declaration is sufficient here
class Container {
private:
std::unique_ptr<ContainedClass> containedPtr; // OK: Pointer to incomplete type
public:
Container();
~Container(); // Must define destructor in .cpp if using unique_ptr to incomplete type
void useContained();
};
#endif // CONTAINER_H
Using a smart pointer to an incomplete type
When using std::unique_ptr
or std::shared_ptr
with an incomplete type, remember that the destructor of the containing class (Container
in this case) must be defined in the .cpp
file where the full definition of ContainedClass
is available. This is because the smart pointer's destructor needs to know how to deallocate the ContainedClass
object, which requires its full definition.
// container.cpp
#include "container.h"
#include "contained_class.h" // Full definition needed here
// Assuming contained_class.h defines ContainedClass
// class ContainedClass { /* ... */ };
Container::Container() : containedPtr(std::make_unique<ContainedClass>()) {}
// Destructor must be defined here, where ContainedClass is complete
Container::~Container() = default;
void Container::useContained() {
if (containedPtr) {
containedPtr->someMethod(); // Accessing members requires full definition
}
}
Implementation of Container methods in .cpp
Scenario 3: Circular Dependencies
Sometimes, two classes depend on each other, leading to a circular #include
problem. Forward declarations are the primary solution here.
Example:
class_a.h
:
#ifndef CLASS_A_H
#define CLASS_A_H
#include "class_b.h" // ERROR: class_b.h might include class_a.h, causing circular include
class ClassA {
ClassB b_member; // Requires full definition of ClassB
};
#endif // CLASS_A_H
class_b.h
:
#ifndef CLASS_B_H
#define CLASS_B_H
#include "class_a.h" // ERROR: class_a.h might include class_b.h, causing circular include
class ClassB {
ClassA a_member; // Requires full definition of ClassA
};
#endif // CLASS_B_H
Solution: Break the circular dependency by using forward declarations and pointers/references. If ClassA
needs to interact with ClassB
and vice-versa, they should typically hold pointers or references to each other, not direct objects, in their header files.
// Corrected class_a.h
#ifndef CLASS_A_H
#define CLASS_A_H
class ClassB; // Forward declaration
class ClassA {
public:
void interactWithB(ClassB* b_ptr);
private:
// If ClassA needs to own a ClassB, use a smart pointer
// std::unique_ptr<ClassB> b_owner;
};
#endif // CLASS_A_H
// Corrected class_b.h
#ifndef CLASS_B_H
#define CLASS_B_H
class ClassA; // Forward declaration
class ClassB {
public:
void interactWithA(ClassA* a_ptr);
private:
// If ClassB needs to own a ClassA, use a smart pointer
// std::unique_ptr<ClassA> a_owner;
};
#endif // CLASS_B_H
Resolving circular dependencies with forward declarations
Best Practices to Avoid the Error
To minimize encountering the 'invalid use of incomplete type' error, adopt these best practices:
- Include what you use: If you need to create an object, access a member, or call a method of a class, ensure its full definition header is included in the
.cpp
file where that code resides. - Forward declare in headers, define in
.cpp
: In your header files, use forward declarations for classes that are only used as pointers or references. Include the full definition in the corresponding.cpp
file. - Use smart pointers for ownership: When a class needs to own an object of another class, but you want to avoid including the full definition in the header, use
std::unique_ptr
orstd::shared_ptr
. - PIMPL Idiom: For complex classes with many private members, consider the Pointer to Implementation (PIMPL) idiom. This technique completely hides the private implementation details from the header, drastically reducing compilation dependencies and times. It relies heavily on forward declarations and smart pointers.
- Review class design: If you frequently run into circular dependencies or need to include full definitions in headers for many classes, it might be a sign that your class design could be improved.