The purpose of odr-use
Informally, odr-use of a variable means the following:
If any expression anywhere in the program takes the address of or binds a reference directly to an object, this object must be defined.
Clarification in the latest draft
In the latest version of the spec §3.2 has been clarified (see Draft C++14 on GitHub):
2 An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. The set of potential results of an expression e
is defined as follows:
- If
e
is an id-expression (5.1.1), the set contains only e
.
- If
e
is a class member access expression (5.2.5), the set contains the potential results of the object expression.
- If
e
is a pointer-to-member expression (5.5) whose second operand is a constant expression, the set contains the potential results of the object expression.
- If
e
has the form (e1), the set contains the potential results of e1.
- If
e
is a glvalue conditional expression (5.16), the set is the union of the sets of potential results of the second and third operands.
- If
e
is a comma expression (5.18), the set contains the potential results of the right operand.
- Otherwise, the set is empty.
[ Note: This set is a (possibly-empty) set of id-expressions, each of which is either e
or a subexpression of e
.
[ Example: In the following example, the set of potential results of the initializer of n
contains the first S::x
subexpression, but not the second S::x
subexpression.
struct S { static const int x = 0; };
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, so
// a definition is required
—end example ] —end note ]
3 A variable x
whose name appears as a potentially-evaluated expression ex
is odr-used by ex
unless applying the lvalue-to-rvalue conversion (4.1) to x
yields a constant expression (5.19) that does not invoke any nontrivial functions and, if x
is an object, ex
is an element of the set of potential results of an expression e
, where either the lvalue-to-rvalue conversion (4.1) is applied to e
, or e
is a discarded-value expression (Clause 5).
What was the situation in C++11?
§3.2/2 in C++11 reads:
An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.
The problem with these wordings was DR 712. Consider this example:
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) {
return x ? S::a : S::b;
}
Since S::a
and S::b
are lvalues the conditional expression x ? S::a : S::b
is also an lvalue. This means that the lvalue-to-rvalue conversion is not immediately applied to S::a
and S::b
, but to the result of the conditional expression. This means that by the wording of C++11, these static data members are odr-used and a definition is required. But actually only the values are used, hence it is not neccessary to define the static data members - a declaration would suffices. The new wording of draft C++14 solves this.
Does the new wording resolve all issues?
No. In the following example the variable S::a
is still odr-used:
struct S { static constexpr int a[2] = {0, 1}; };
void f() {
auto x = S::a[0];
}
Hence I submitted a new issue to add the following bullet to §3.2/2:
- if
e
is a glvalue subscripting expression (5.2.1) of the form E1[E2]
, the set contains the potential results of E1
.