Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Speaking Standardese: the One Definition Rule

Last updated Jan 1, 2003.

The One Definition Rule (ODR), as the name suggests, requires that an object with external linkage, a non-inline function, a class, an enumeration or a template shall have exactly one definition in the program.

Rationale

There can be more than one definition in the program of a class type, enumeration type, inline function with external linkage, class template, non-static function template, static data member of a class template, member function template, or template specialization for which some template parameters are not specified provided that:

  • Each definition appears in a different translation unit
  • All definition referring to the same entity must be identical

In other words, a program may consist of multiple translation units, each of which containing the same definition of a certain entity (class template, enum, class, inline function etc.) Later, at linkage time, these multiple definitions are collapsed into a single definition. Obviously, the separately-compiled definitions of the same entity must be identical but what exactly does "identical definition" mean? An entity D that is defined in more than one translation unit satisfies the ODR if it meets the criteria listed below.

Token-by-Token Identity

Each definition of D must consist of the same sequence of tokens. For example, the following definitions of class S have the same sequence of tokens although they look different to the human eye:

//translation unit 1
class S {};
//translation unit 2
#define X
class S{
X
};

Function Call Resolution

In each definition of D, overloaded operators referred to, implicit calls to conversion functions, constructors, operator new and operator delete functions shall refer to the same function, or to a function defined within the definition of D (i.e., an inline function). Consider:

//translation unit 1
class S 
{
public:
 S(int); 
 ~S(){cout<<"goodbye!"<<endl;}
};
//translation unit 2
class S{
public:
 S(int); 
 ~S(){cout<<"goodbye!"<<endl;}
};

The two definitions of S are identical.

Default Arguments Identity

In each definition of D, a default argument used by a function call is treated as if its token sequence were present in the definition of D. In other words, default arguments are subjected to the previous two requirements. Consider:

//translation unit 1
const int ZERO=0;
class S 
{
public:
 S(int n=ZERO); 
 ~S(){cout<<"goodbye!"<<endl;}
};
//translation unit 2
class S{
public:
 S(int n=0) {}
 ~S(){cout<<"goodbye!"<<endl;}
};

Both definitions of S are identical.

Implicitly-Defined Constructor Identity

A class D that has an implicitly defined constructor is treated as if its constructor is defined in every translation unit. The implicitly defined constructor must call the same constructor(s) of D’s base class(es) and member objects. Consider:

// translation unit 1
struct A {
 A(int);
 A(int, int);
};
A::A(int = 0) {}
class D: public A {};
D d; // implicitly-defined D() calls A(int)

// translation unit 2
struct A {
 A(int);
 A(int, int);
};
A::A(int = 0, int = 0) {} 
class D: public A { }; // // implicitly-defined D() calls A(int, int)

Are the two definitions of D identical? Each definition of D has an implicitly-defined default constructor that invokes a default constructor of the base class A. In order to qualify as identical, the implicitly-defined constructors of D must call the same constructor of A in both translation units. This requirement isn’t met. In translation unit 1, D’s default constructor invokes the default constructor A(int), whereas in translation unit 2, it invokes A(int, int). Hence, the two definitions of D violate the ODR.

Template Identity

If D is a template that is defined in more than one translation unit, then the four requirements above shall apply to names from D’s enclosing scope used in the template definition (i.e., to independent names), and also to dependent names at the point of instantiation.

ODR Violations

If the definitions of D satisfy all the requirements above, then the program shall behave as if there were a single definition of D. Technically speaking, either the linker will discard all copies but one, or there will be multiple definitions of D in the program, but the program will behave as if there were only one definition of D.

If the definitions of D do not satisfy these requirements, then the behavior is undefined. Usually, compilers can’t detect ODR violations (unless you’re using a tool that analyzes the entire program). Therefore, no diagnostic is required in ODR violation cases.