Fixing "multiple definition" Linker Errors in C++: ODR, Header Definitions and inline

Key takeaways

Why code that compiles can still fail at link time with "multiple definition": the One Definition Rule, the common causes (functions, globals, static members and full template specializations defined in headers), and the fixes: inline, extern, or moving the definition to one .cpp.

You split a project into several files, every .cpp compiles cleanly, and then the final step fails:

// utils.h
#pragma once
#include <iostream>

void foo() {            // a definition, not just a declaration
    std::cout << "foo\n";
}

// main.cpp
#include "utils.h"
int main() { foo(); }

// other.cpp
#include "utils.h"
void bar() { foo(); }
$ g++ main.cpp other.cpp
ld: other.o:other.cpp:(.text+0x0): multiple definition of `foo()'; main.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

On MSVC the same problem shows up as:

other.obj : error LNK2005: "void __cdecl foo(void)" (?foo@@YAXXZ) already defined in main.obj
fatal error LNK1169: one or more multiply defined symbols found

The message is precise once you know how to read it. The symbol in backticks is the duplicate; the first object file listed is the second definition the linker found, and “first defined here” names the object file where it saw it the first time. The section name tells you what kind of thing it is: .text is code (a function), .data is an initialized variable, .bss is a zero-initialized variable.

Why the compiler cannot catch this

The compiler only ever sees one translation unit at a time: a single .cpp with all its #includes pasted in. From main.cpp’s point of view there is exactly one foo(), and the same is true for other.cpp. Both object files are individually correct. Only the linker sees the whole program, and only the linker notices that two object files both claim to provide the symbol foo().

This is also why #pragma once and #ifndef guards do nothing here. A guard prevents the header from being pasted in twice within one translation unit. It has no memory across translation units, because each .cpp is compiled by a separate compiler invocation, possibly in parallel. Two .cpp files including the header means two copies of the definition, guards or not.

The One Definition Rule

The rule being enforced is the One Definition Rule (ODR). Simplified:

  1. An ordinary (non-inline) function or variable with external linkage must be defined exactly once in the whole program, if it is used.
  2. Classes, enums, inline functions, inline variables and templates may be defined in several translation units, as long as every definition is token-for-token identical.

Rule 2 is what makes headers work at all: a class definition in a header appears in every .cpp that includes it, and that is fine. Rule 1 is what you are violating when the linker complains. The fix is always one of two things: make sure there is only one definition, or move the entity into the category covered by rule 2.

There is a nasty corner of rule 2. If two translation units contain different definitions of the same inline function or class (for example because a macro changed between includes), that is an ODR violation too, but the standard says “no diagnostic required”. The linker silently keeps one copy and discards the others. You get no error, just a program that sometimes runs the wrong code. The loud “multiple definition” error is, in comparison, the friendly case.

Cause 1: a non-inline function defined in a header

This is the example above and by far the most common case.

Fix A: declare in the header, define in one .cpp. This is the default choice for anything non-trivial.

// utils.h
#pragma once
void foo();             // declaration only

// utils.cpp
#include "utils.h"
#include <iostream>
void foo() { std::cout << "foo\n"; }

Besides fixing the error, this keeps <iostream> out of every file that includes utils.h, and changing the body of foo recompiles one file instead of all of them.

Fix B: mark it inline. This is right for small functions you genuinely want visible in the header, and for header-only libraries.

// utils.h
#pragma once
#include <iostream>
inline void foo() { std::cout << "foo\n"; }

inline in modern C++ is mostly not an optimization hint. Its guaranteed meaning is “this definition may appear in multiple translation units; the linker should merge them”. The compiler emits the function into a special section (a COMDAT or “weak” symbol), and the linker keeps one copy.

Member functions defined inside the class body are implicitly inline, which is why this never triggers the error:

struct Point {
    int x, y;
    int sum() const { return x + y; }   // implicitly inline, fine in a header
};

But a member function defined outside the class body in a header is not:

// point.h
struct Point { int x, y; int sum() const; };
int Point::sum() const { return x + y; }   // multiple definition if included twice

Either add inline in front of the out-of-class definition or move it to point.cpp.

Fix C: static (or an unnamed namespace). This gives the function internal linkage, so each translation unit has its own private copy and nothing clashes. It works, but it hides the problem rather than solving it: each object file now carries a duplicate of the code, and a function-local static variable inside it exists once per .cpp, not once per program.

Cause 2: a global variable defined in a header

// config.h
int maxConnections = 100;   // definition

Every includer defines maxConnections, and the linker reports it in .data (or .bss if it is zero-initialized).

Fix A: extern declaration plus one definition.

// config.h
extern int maxConnections;   // declaration: "it exists somewhere"

// config.cpp
#include "config.h"
int maxConnections = 100;    // the single definition

Fix B: an inline variable (C++17).

// config.h
inline int maxConnections = 100;   // one variable shared by every TU

What you should not do is make it static, because the difference is not just cosmetic. With a header containing both kinds:

// counters.h
static int hits = 0;       // one copy per .cpp
inline int ihits = 0;      // one copy per program

// other.cpp
#include "counters.h"
void inc() { ++hits; ++ihits; }

// main.cpp
#include "counters.h"
#include <cstdio>
void inc();
int main() { inc(); std::printf("%d %d\n", hits, ihits); }

This compiles, links and prints 0 1. inc() incremented other.cpp’s private hits; main.cpp reads its own, which is still zero. The inline variable is genuinely shared.

Note that namespace-scope const and constexpr variables already have internal linkage in C++, so const int kMax = 100; in a header never produces this error. Each TU gets its own copy, which is harmless for a constant (though taking its address in two files gives two different addresses). A non-const array next to it, int buffer[64];, does trigger the error.

Cause 3: static data members defined in a header

// cfg.h
struct Cfg { static int limit; };
int Cfg::limit = 10;          // definition, in .data
multiple definition of `Cfg::limit'; main.o:main.cpp:(.data+0x0): first defined here

Move int Cfg::limit = 10; into cfg.cpp, or in C++17 write inline static int limit = 10; inside the class. static constexpr data members are implicitly inline since C++17, so static constexpr int limit = 10; in the class is also fine.

Cause 4: a full template specialization in a header

Templates are allowed in headers, so people are surprised by this one:

// size.h
template <class T> int sz() { return sizeof(T); }   // fine
template <> int sz<void>() { return 0; }            // not fine
multiple definition of `int sz<void>()'

A full explicit specialization is no longer a template; it is an ordinary function that happens to have a template-looking name, and rule 1 applies to it. Write template <> inline int sz<void>() { return 0; }, or declare the specialization in the header and define it in one .cpp.

Cause 5: the same code compiled twice

Sometimes no header is involved at all:

  • #include "helpers.cpp" somewhere, while helpers.cpp is also listed as a source file.
  • The same source file listed twice in a CMake target, or in two static libraries that both get linked into one executable.
  • A generated file (protobuf, Qt moc output) added to the build both automatically and by hand.

The object file names in the error are the clue: if both lines mention the same source name, or one of them is an object inside a .a/.lib you did not expect, look at the build system rather than the code.

A note on C code and GCC 10

If you build C (not C++) and a project that used to link started failing with “multiple definition” after moving to GCC 10 or newer, the cause is that GCC 10 switched its default from -fcommon to -fno-common. Old C code that wrote int x; in a header relied on “common” symbols being merged. The proper fix is the extern pattern above; -fcommon restores the old behaviour as a stopgap. C++ never had this leniency.

Where I have seen this go wrong

The failure mode I find most confusing is not the error itself but the “fix” that follows it. Someone gets the error for a global in a header, adds static because it makes the linker quiet, and ships. Weeks later a setting “doesn’t take effect”: one module writes to its copy of the variable, another module reads its own untouched copy. Nothing crashes and nothing warns, which makes it much harder to track down than the original link error. When I see static on a mutable variable in a header now, I treat it as a bug until proven otherwise.

The other one is a header that has worked for years because only one .cpp included it. The day a second file includes it, the build breaks in a file nobody touched, and the diff that triggered it looks completely innocent. If a header contains any non-inline definitions, it is a latent link error waiting for its second includer.

Quick decision guide

What is in the headerFix
Non-trivial functionDeclaration in header, definition in one .cpp
Small function you want in the headerinline
Out-of-class member function bodyinline, or move to .cpp
Mutable global variableextern in header + one definition, or C++17 inline variable
Constantconstexpr (or inline constexpr if a single address matters)
Static data memberDefine in .cpp, or inline static / static constexpr in the class
Full template specializationinline, or define in .cpp

FAQ

Q. How do I share a global variable across .cpp files without a multiple definition error?

A. Put extern int g_counter; in the header and int g_counter = 0; in exactly one .cpp, or since C++17 write inline int g_counter = 0; in the header. Avoid static for this: each file would get a separate counter.

Q. The error names a symbol I never defined twice. Where do I look?

A. Check whether the definition lives in a header, whether a .cpp is being #included, and whether the same source is compiled into two libraries. The two object file names in the message tell you which translation units to compare.