Definitions and ODR
Definitions and ODR
Definitions
are declarations that fully define the entity introduced by the declaration. Every declaration is a definition, except for the following:
- A function declaration without a function body
int f(int // declares, but doesn't define f
- Any declaration with an
extern
storage class specifier or with a language linkage specifier (such asextern "C"
) without an initializer
extern const int a; // declares, but doesn't define a
extern const int b = 1; // defines b
- Declaration of a non-inline (since C++17) static data member inside a class definition
struct S {
int n; // defines S::n
static int i; // declares, but doesn't define S::i
inline static int x; // defines S::x
}; // defines S
int S::i; // defines S::i
(deprecated) Namespace scope declaration of a static data member that was defined within the class with the constexpr specifier struct S { static constexpr int x = 42; // implicitly inline, defines S::x }; constexpr int S::x; // declares S::x, not a redefinition | (since C++17) |
---|
- (deprecated) Namespace scope declaration of a static data member that was defined within the class with the
constexpr
specifier
struct S { static constexpr int x = 42; // implicitly inline, defines S::x }; constexpr int S::x; // declares S::x, not a redefinition
(since C++17)
- Declaration of a class name (by forward declaration or by the use of the elaborated type specifier in another declaration)
struct S; // declares, but doesn't define S
class Y f(class T p // declares, but doesn't define Y and T (and also f and p)
An opaque declaration of an enumeration enum Color : int; // declares, but doesn't define Color | (since C++11) |
---|
- An opaque declaration of an enumeration
enum Color : int; // declares, but doesn't define Color
(since C++11)
- Declaration of a template parameter
template<typename T> // declares, but doesn't define T
- A parameter declaration in a function declaration that isn't a definition
int f(int x // declares, but doesn't define f and x
int f(int x) { // defines f and x
return x+a;
}
- A typedef declaration
typedef S S2; // declares, but doesn't define S2 (S may be incomplete)
An alias-declaration using S2 = S; // declares, but doesn't define S2 (S may be incomplete) | (since C++11) |
---|
- An alias-declaration
using S2 = S; // declares, but doesn't define S2 (S may be incomplete)
(since C++11)
- A using-declaration
using N::d; // declares, but doesn't define d
Declaration of a deduction guide (does not define any entities) | (since C++17) |
---|---|
A static_assert declaration (does not define any entities) An attribute declaration (does not define any entities) | (since C++11) |
- Declaration of a deduction guide (does not define any entities)
(since C++17)
- A static_assert declaration (does not define any entities)
(since C++11)
- An empty declaration (does not define any entities)
An explicit instantiation declaration (an "extern template") extern template f | (since C++11) |
---|
- An explicit instantiation declaration (an "extern template")
extern template f<int, char>; // declares, but doesn't define f<int, char>
(since C++11)
- An explicit specialization whose declaration is not a definition.
template<> struct A<int>; // declares, but doesn't define A<int>
An asm declaration does not define any entities, but it is classified as a definition.
Where necessary, the compiler may implicitly define the default constructor, copy constructor, move constructor, copy assignment operator, move assignment operator, and the destructor.
If the definition of any object results in an object of incomplete type, the program is ill-formed.
One Definition Rule
Only one definition of any variable, function, class type, enumeration type, or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).
One and only one definition of every non-inline function or variable that is odr-used
(see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
For an inline function or inline variable (since C++17), a definition is required in every translation unit where it is odr-used
.
One and only one definition of a class is required to appear in any translation unit where the class is used in a way that requires it to be complete.
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, partial template specialization, as long as all of the following is true:
- each definition consists of the same sequence of tokens (typically, appears in the same header file)
If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.
Note: in C, there is no program-wide ODR for types, and even extern declarations of the same variable in different translation units may have different types as long as they are compatible. In C++, the source-code tokens used in declarations of the same type must be the same as described above: if one .cpp file defines struct S { int x; };
and the other .cpp file defines struct S { int y; };
, the behavior of the program that links them together is undefined. This is usually resolved with unnamed namespaces.
ODR-use
Informally, an object is odr-used if its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
struct S {
static const int x = 0; // static data member
// a definition outside of class is required if it is odr-used
};
const int& f(const int& r
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x // S::x is odr-used here: a definition is required
Formally,
1) a variable x
in a potentially-evaluated
ex
pression ex
is odr-used unless both of the following are true:
- applying lvalue-to-rvalue conversion to
x
yields a constant ex
pression that doesn't invoke non-trivial functions
struct S { static const int x = 1; };
int f() { return S::x; } // does not odr-use S::x
e
ithe
rx
is not an obje
ct (that is,x
is a re
fe
re
nce
) or, ifx
is an obje
ct, it is one
of the
potential results
of a large
re
x
pre
ssione
, whe
re
that large
re
x
pre
ssion ise
ithe
r a discarde
d-value
e
x
pre
ssion or an lvalue
-to-rvalue
conve
rsion
struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
2) this is odr-used if it appears as a potentially-evaluated
expression (including the implicit this in a non-static member function call expression)
In the
de
finitions above
, potentially-evaluated
me
ans the
e
xpre
ssion is not an une
valuate
d ope
rand (or its sube
xpre
ssion), such as the
ope
rand of size
of and a set of potential results
of an e
xpre
ssion e
is a (possibly e
mpty) se
t of id-e
xpre
ssions that appe
ar within e
, combine
d as follows:
- If
e
is an id-e
xpre
ssion, the
e
xpre
ssione
is its only pote
ntial re
sult
If e is an array subscript expression (e1e2) and the operand (e1) is array, that array is included in the set | (since C++17) |
---|
- If
e
is an array subscripte
xpre
ssion (e1[e2]
) and the
ope
rand (e1
) is array, that array is include
d in the
se
t
(since C++17)
- If e is a class member access expression (e1.e2 or e1->e2), the potential results of the object expression e1 is included in the set.
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) {
return x ? S::a : S::b;
// x is a part of the subexpression "x" (to the left of ?),
// which applies lvalue-to-rvalue conversion, therefore x is not odr-used
// S::a and S::b are lvalues, and carry over as "potential results" to the result
// of the glvalue conditional
// That result is then subject to lvalue-to-rvalue conversion requested
// to copy-initialize the return value, therefore S::a and S::b are not odr-used
}
4) Functions are ODR-used if
- A function whose name appears as a potentially-evaluated expression (including named function, overloaded operator, user-defined conversion, user-defined placement forms of operator new, non-default initialization) is odr-used if it is selected by overload resolution, except when it is an unqualified pure virtual member function or a pointer-to-member to a pure virtual function (since C++17).
In all cases, a constructor selected to copy or move an object is odr-used even if copy elision takes place.
References
- C++11 standard (ISO/IEC 14882:2011):
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.