Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is a variable odr-used in C++14?

The C++14 draft (N3936) states in §3.2/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial 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).

This doesn't make any sense to me: If an expression e is a discarded-value expression depends on the context, in which e is used. Every expression used in an expression-statement (§6.2) is a discarded-value expression. If the lvalue-to-rvalue conversion is applied to e also depends on the context e is used in.

Moreover, what does it mean for an expression to be in the set of potential results of another expression. One needs a notion of equality of expressions to be able to determine membership of a set. But we don't have referential transparency, so I cannot see how this could be achieved.

Why was this changed from C++11 to C++14? And how should this be interpreted? As it stands, it doesn't make sense.

like image 638
MWid Avatar asked May 06 '14 10:05

MWid


1 Answers

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.
like image 58
MWid Avatar answered Oct 21 '22 22:10

MWid