Question

C++ Header Include Order: Rules, Reasons, and Best Practices

ccppinclude

Question

What order should C and C++ header files be included in, and what are the reasons for placing one header before another?

For example, should system headers, C++ standard library headers, Boost headers, and project-local headers appear first or last in an include list?

Short Answer

C++ does not require one universal ordering for #include directives. The important rule is that every header should compile with the declarations it directly needs. A consistent project order—often the current module's header first, followed by project, third-party, standard-library, and system headers—makes missing dependencies easier to find and code easier to review.

Concept

#include is handled by the preprocessor before C++ compilation. Conceptually, it copies the included header's contents into the current source file.

That means include order can affect whether a file compiles: a header may accidentally rely on another header having already been included. This is called a hidden or transitive dependency.

For example, std::string is declared by <string>. If a header uses std::string, that header should include <string> itself. It should not rely on some source file including <string> earlier.

// user_profile.h
#pragma once

#include <string>  // Direct dependency: this header uses std::string.

struct UserProfile {
    std::string name;
};

The exact visual grouping is a project convention, not a C++ language rule. The real goals are:

  • Make each header self-contained.
  • Make direct dependencies visible.
  • Detect missing includes early.
  • Keep include lists predictable for readers and tools.
  • Follow the existing repository's formatter and style guide.

A common convention in .cpp files is to include the matching header first. If user_profile.cpp starts with #include "user_profile.h", then user_profile.h must compile without being rescued by unrelated earlier includes in the implementation file.

Mental Model

Think of a header as a recipe card. If the recipe says “add flour,” the card must list flour in its own ingredients. It should not assume that someone read a different recipe card first.

Include order is the order in which recipe cards are placed in a stack. A badly written card may appear to work only because an earlier card happened to provide a missing ingredient. Including the current file's own header first is a quick test: does this card bring everything it needs?

Take Quiz

Syntax and Examples

Use #include <...> for headers found through configured system or library include paths, and #include "..." for project headers according to your project's conventions.

A typical .cpp include layout is:

#include "user_profile.h"        // Matching header for this implementation file

#include "project/logger.h"      // Other project headers

#include <boost/optional.hpp>     // Third-party library headers

#include <iostream>               // C++ standard library headers
#include <string>

#include <sys/types.h>            // Platform/system headers, when needed

Another widely used convention places groups in this order:

  1. Matching header
  2. C and C++ standard-library headers
  3. Third-party headers
  4. Project headers

Either can be valid. What matters is consistency and the dependency rule: every file includes what it directly uses.

For a header, include only what is necessary for declarations in that header:

// report.h
#pragma once

#include <string>
#include <vector>

class Report {
public:
    void addLine(std::string line);
    const std::vector<std::string>& lines() const;

private:
    std::vector<std::string> lines_;
};

report.h directly uses std::string and std::vector, so it includes both <string> and <vector>.

Step by Step Execution

Consider this fragile code:

// message.h
#pragma once

struct Message {
    std::string text;
};

// message.cpp
#include <string>
#include "message.h"

Step by step:

  1. The preprocessor starts reading message.cpp.
  2. <string> is inserted first, so std::string becomes known.
  3. message.h is inserted next.
  4. Message compiles because <string> happened to be included first.
  5. Another source file that includes message.h without including <string> first can fail to compile.

Fix the dependency at its source:

// message.h
#pragma once

#include <string>

struct Message {
    std::string text;
};

// message.cpp
#include "message.h"

Now message.h works wherever it is included. Including the matching header first in message.cpp also exposes this kind of mistake immediately.

Real World Use Cases

  • Application modules: account.cpp includes account.h first so the public header is tested in normal builds.
  • Libraries and SDKs: Public headers explicitly include declarations they expose, so customers can include them safely.
  • Cross-platform code: Platform headers may define macros or types, so projects isolate them in a consistent group or wrapper header.
  • Third-party integrations: Boost, Qt, or other external headers are grouped separately, making external dependencies easy to identify.
  • Build troubleshooting: Reordering includes can reveal a header that depends accidentally on another header's transitive includes.
  • Automated formatting: Tools such as clang-format can sort and separate include groups according to repository rules.
Take Quiz

Real Codebase Usage

In a production C++ codebase, the first priority is the repository's existing style. Do not reorder a large file just to apply a different personal convention.

Common patterns include:

  • Matching header first: In widget.cpp, write #include "widget.h" before other includes. This validates the public interface independently.
  • Direct includes: If a header uses std::unique_ptr, include <memory> in that header. If it uses std::vector, include <vector>.
  • Forward declarations when appropriate: A header can forward-declare a class when it only stores a pointer or reference to it. This can reduce coupling and rebuild time.
  • Include-what-you-use: Include declarations that the current file needs directly instead of relying on implementation details of other headers.
  • Stable grouping: Separate project, third-party, standard-library, and platform headers with blank lines if that is the local convention.
  • Tool-enforced sorting: Configure clang-format or a linter so formatting is repeatable rather than debated during review.

Example with a forward declaration:

// order_service.h
#pragma once

#include <memory>

class Logger;  // A declaration is enough for a pointer member.

class OrderService {
public:
    explicit OrderService(std::shared_ptr<Logger> logger);

private:
    std::shared_ptr<Logger> logger_;
};

The .cpp file needs the full Logger definition when calling its members:

#include "order_service.h"

#include "project/logger.h"

Common Mistakes

Relying on an indirect include

Broken header:

// record.h
#pragma once

struct Record {
    std::vector<int> values;
};

It may compile in one file only because another included header currently includes <vector>. Fix it directly:

#include <vector>

Including the matching header after unrelated headers

#include <vector>
#include "record.h"

This can hide the missing <vector> in record.h. Prefer this in record.cpp:

#include "record.h"

Then add missing direct dependencies to record.h.

Treating angle brackets and quotes as a dependency rule

<header> versus "header" mainly affects where the compiler searches. It does not decide whether a dependency is direct or indirect. Follow your build system and project convention.

Adding headers "just in case"

Adding a giant common header may make compilation work, but it hides real dependencies and increases compile time. Include the smallest header that provides the declaration you use.

Forward-declaring a type when a complete type is required

A forward declaration is not enough when a class contains another class by value:

class Engine;

class Car {
    Engine engine_; // Error: Engine must be complete here.
};

Include the header defining Engine instead. Forward declarations are commonly suitable for pointers and references.

Ignoring the project style guide

There is no language-mandated global order for standard, third-party, and local headers. Consistent code is usually more valuable than changing a file to match a different convention.

Comparisons

ApproachBest useMain benefitLimitation
Matching header first.cpp implementation filesFinds missing dependencies in the matching headerDoes not by itself make all headers self-contained
Standard/third-party/project groupsLarge projectsMakes dependencies easy to scanExact group order is conventional
Direct includeA file uses a declarationCorrect, explicit dependencyCan add compilation work if overused
Forward declarationOnly pointers/references to a class are exposedReduces coupling and included codeCannot replace a full definition when storing by value or accessing members inline
One umbrella/common headerSpecial controlled cases, such as precompiled headersMay improve build setup in some environmentsEasily hides dependencies; should not replace direct includes

A useful distinction:

  • Include order is mostly a readability and diagnostic convention.
  • Direct dependencies are a correctness requirement for reliable headers.
Take Quiz

Cheat Sheet

  • C++ does not prescribe one required include order.
  • Follow the repository's style guide and formatter.
  • Each header should include what it directly needs.
  • In thing.cpp, commonly put #include "thing.h" first.
  • Group related categories with blank lines if the project does so.
  • Use <...> and "..." according to project/build conventions; this is mainly about header search paths.
  • Include <string> for std::string, <vector> for std::vector, <memory> for smart pointers, and so on.
  • Use a forward declaration only when a complete type is not required in the header.
  • Do not depend on transitive includes from standard-library, third-party, or project headers.
  • Use #pragma once or conventional include guards to prevent repeated inclusion of the same header in one translation unit.

FAQ

Is there an official C++ include order?

No. The C++ language does not require an order such as standard library first or local headers first. Projects choose conventions.

Why include the matching header first in a C++ source file?

It reveals whether that header is self-contained. If the header needs <string> but forgot to include it, the build fails at the right place.

Should a header include every header used by its .cpp file?

No. A header should include only declarations needed by its own public declarations and inline definitions. Implementation-only dependencies belong in the .cpp file.

Can I rely on a standard-library header including another standard-library header?

No. Include the specific standard header for every facility you use. Transitive includes are not a dependable interface.

Should Boost headers go before standard-library headers?

Either order can be acceptable if it matches the project convention. Keep third-party headers grouped separately and make all direct dependencies explicit.

When should I use a forward declaration instead of an include?

Use one when the header only needs to mention a class through a pointer or reference. Include the full definition when storing it by value, inheriting from it, or using members in inline code.

Does #pragma once determine include order?

No. It prevents repeated processing of the same header within one translation unit. It does not provide missing declarations or solve dependency order.

Take Quiz

Mini Project

Description

Create a small C++ module with a public header and implementation file. The exercise demonstrates self-contained headers, matching-header-first ordering, and separating interface dependencies from implementation dependencies.

Goal

Build a GreetingService that returns a greeting and compiles without relying on accidental transitive includes.

Requirements

Requirement 1

Take Quiz

Keep learning

Related questions

View more