Skip to content

One Definition Rule (ODR)

Published On:
May 4, 2025
Last Updated:
May 5, 2025

The One Definition Rule (ODR) is a C++ language rule that states:

  1. Each class/struct or inline function must only have one definition in the entire program.
  2. Templates and types must have only one unique definition per translation unit.

Well hang on, what if I define a class in a header file and include the header file in different .cpp files? Clearly this is not illegal since it is common practise everywhere. Here is where there is an exception, classes are allowed to be defined multiple times in different translation units, as long as the definitions are identical!

Classes

The ODR rule means that the following is illegal, because it defines MyClass twice in the same translation unit:

main.cpp
class MyClass {};
class MyClass {}; // Illegal!

While this is illegal, it is not normally a problem since the compiler can easily detect this and give you an error (I tested this with GCC v11.4.0, see This is a placeholder for the reference: fig-multiple-definitions-in-one-file-error). Header guards protect the same header file (and thus the same class) from being defined multiple times in the same translation unit (header guards also do useful things like preventing infinite re-inclusion).

The error message from the compiler when you define the same class twice in the same .cpp file.

What IS more common is defining the same class multiple times in different translation units. This is not picked up by the compiler or linker (it’s a linker thing since it would be when combining multiple .o files together). I tested this with GCC v11.4.0 and got no error even though I had two different classes defined with the same name.

Working Example

Here is an example of a C++ application which does not produce any compiler or linker errors (or warnings), but violates the ODR and clearly shows unexpected behaviour at runtime as a result. Let’s first define an interface class:

Interface.h
#pragma once
// Abstract base class
class Base {
public:
virtual int getInt() = 0;
virtual ~Base() = default;
};
extern Base *createChild1();
extern Base *createChild2();

Now let’s create two implementations of this interface, but both name the child class Child. Here is the first implementation:

Child1.cpp
#include "Interface.h"
// Violates the ODR, multiple definitions of "Child"
class Child : public Base {
public:
int getInt() override {
// This version returns 1
return 1;
}
};
Base *createChild1() {
return new Child();
}

Now let’s create a second implementation of the interface, again named Child (which violates the ODR):

Child2.cpp
#include "Interface.h"
// Violates the ODR, multiple definitions of "Child"
class Child : public Base {
public:
int getInt() override {
// This version returns 2
return 2;
}
};
Base *createChild2() {
return new Child();
}

And finally our main.cpp file looks like this:

main.cpp
#include <iostream>
#include "Interface.h"
int main() {
Base *child1 = createChild1();
Base *child2 = createChild2();
std::cout << "child1: " << child1->getInt() << std::endl;
std::cout << "child2: " << child2->getInt() << std::endl;
return 0;
}

When built and run, this code will normally output:

child1: 1
child2: 1

Which clearly shows the problem. child2 is meant to return 2! But it returns 1 because we have two classes called Child, and the compiler only creates a vtable for one of them.

I hit the ODR issue when I was building a benchmarking executable for testing different C++ callback techniques. I defined a number of MyClass classes in various .cpp files (I skipped writing these in header files for brevity, and they weren’t being used anywhere else) for testing example code. Most of the time this caused no problem at all, but for 1 of the approx. 10 examples the program would segfault. After posting the problem online someone pointed out it was probably due to the ODR issue. Wrapping all the MyClass definitions in a anonymous namespace (namespace { ... }) fixed the problem.

inline is a core concept to ODR. Anything that is “inlined” is allowed to have multiple definitions. The compiler will pick one and “throw out” the rest. Some things are implicitly inline, others can be made inline with the inline keyword.

Implicitly inline:

  • In class function definitions
  • Templated classes

Free Functions

Functions that are not members of a class are called free functions. Unless the are declared with inline, they are not allowed to be defined more than once in a program. Luckily, most compilers can easily deduct duplicate function definitions and will throw an error, as shown in This is a placeholder for the reference: fig-multiple-definition-of-my-function-linker-error.

The error message from the linker when you define the same function twice in two different .cpp files.

Static Class Members

Static class members can be defined in the header file, but you must make sure that the definition is only in one .cpp file:

MyClass.h
class MyClass {
static int count; // Declaration
};
MyClass.cpp
int MyClass::count = 0; // Definition (must be in only one .cpp file)