2

The Example in [basic.def.odr]/2 starts with the following sentence:

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.

From the definitions in this paragraph, how can we deduce that the initializer of n contains the first S::x subexpression, but not the second S::x subexpression?

Edit See below the remaining part of the Example referred above:

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
Leon
  • 868
  • 4
  • 12
  • 1
    The question is unanswerable by people without the C++14 standard. You could help by showing the code that you are asking about. Then it could be answerable by people without access to the C++14 standard but who have a knowledge of the C++14 standard. – Jonathan Leffler May 02 '15 at 00:59
  • @JonathanLeffler It is unanswerable for people *with* (only) the C++14 Standard. This example appears only in more recent drafts (which are publicly available). But I agree that the example should be part of the question. – dyp May 02 '15 at 00:59
  • @dyp: So the tag and title are incorrect (it should be C++17), and the question should point to the recent draft that people need to refer to. Basically, when you ask a question, you need to help people to help you! It also radically changes the question. If it's a draft, there could still be bugs in the specification. It is less likely (though not impossible) if it is in a standard. – Jonathan Leffler May 02 '15 at 01:01
  • This appears in n4140, which is C++14. Btw "how can we deduce that the initializer of n contains" is incorrect -- It's not about what initializer contains, it's about what it returns (potentially) – Cubbi May 02 '15 at 01:02
  • @JonathanLeffler n4140 is freely available and contains the same text as C++14. – M.M May 02 '15 at 01:03
  • @Cubbi I have the C++14 IS here, and there is no example in [basic.def.odr]p1-3. – dyp May 02 '15 at 01:03
  • IMHO it's safe to assume that anyone who can answer a language-lawyer question will have a copy of the standard – M.M May 02 '15 at 01:04
  • 1
    Thanks, @MattMcNabb: I know where to find the documents. AFAIK, there is not yet a sanely priced official (ISO or ANSI or ...) C++14 PDF available (but I haven't checked this month). And it is not completely safe to use committee drafts rather than the final standard. The question should be comprehensible to subsequent people reading it -- they should not have to go find the standard to make use of the answers. I'm sorry, but the question here should include the example code, though not necessarily very much more than that. – Jonathan Leffler May 02 '15 at 01:04
  • @LeonTrotski can you clarify whether you are looking at C++14 text, or a particular "N" document? – M.M May 02 '15 at 01:07
  • @Cubbi N4140 includes additional editorial changes compared to C++14, which it appears includes adding this example. – T.C. May 02 '15 at 01:10
  • @MattMcNabb I'm referring to the N4140 draft – Leon May 02 '15 at 01:13

1 Answers1

12

I'm using a recent github draft based on N4296. The actual C++14 International Standard does not contain this example, nor the numbering of bullet points. The specification relevant here is effectively the same.

We decompose the expression in the initializer: b ? (1, S::x) : f(S::x)

The expression (1, S::x) is an lvalue of type int const. The expression f(S::x) is a postfix-expression, an lvalue of type int const.

Hence the expression b ? (1, S::x) : f(S::x) is an lvalue of type int const. It therefore fulfils [basic.def.odr]p2.5, and the set of potential results is the union of the sets of potential results of the sub-expressions (1, S::x) and f(S::x).

For the first sub-expression (1, S::x), we strip the parentheses via p2.4. The result 1, S::x is a comma expression. We apply p2.6 and get S::x. Now, p2.1 applies and tells us that this first occurrence is part of the set of potential results of the initializer.

For the second sub-expression f(S::x), only p2.7 applies. Its set of potential results is empty, so it doesn't add anything to the set of potential results of the initializer.


As for the odr-use of S::x, [basic.def.odr]p3

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression 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 is applied to e, or e is a discarded-value expression.

Let's split this into steps: The occurrence of a variable x in an expression ex constitutes an odr-use unless:

  1. Either ex is not potentially evaluated, or
  2. All of the following must be fulfilled:
    1. "applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions" and
    2. "ex is an element of the set of potential results of an expression e" and either of the following holds:
      1. "either the lvalue-to-rvalue conversion is applied to e"
      2. "or e is a discarded-value expression"

Note that point 2 means "is an element of the set of potential results of ANY expression e [where e fulfils certain requirements]", rather than "all expressions e it is part of". Further discussion can be found on the std-discussion mailing list.

Applying the steps to the second occurrence of `S::x`

It is part of the expressions S::x, f(S::x), b ? (1, S::x) : f(S::x).

  1. False (since all of these expressions are potentially evaluated), or
  2. All of the following must be fulfilled:
    1. True (since applying the l-t-r conversion to S::x yields a constant expression that does not invoke any functions) and
    2. The only expression where the second occurrence of S::x is an element of the set of potential results is S::x itself. It is not part of the potential results of f(S::x). Either of the following must hold:
      1. either false (since the lvalue-to-rvalue conversion is not applied when binding S::x to the function parameter of f)
      2. or false (since S::x is not a discarded-value expression)

The exception does not apply, S::x is odr-used via its second occurrence.

Applying the steps to the first occurrence of `S::x`

It is part of the expressions S::x, 1, S::x, (1, S::x), b ? (1, S::x) : f(S::x).

  1. False (since all of these expressions are potentially evaluated), or
  2. All of the following must be fulfilled:
    1. True (since applying the l-t-r conversion to S::x yields a constant expression that does not invoke any functions) and
    2. The first occurrence of S::x is an element of the set of potential results of all the expressions it is part of within the initializer. Either of the following must hold:
      1. true - The lvalue-to-rvalue conversion is certainly not applied to the expressions S::x, 1, S::x, (1, S::x). It can be argued that it is applied to b ? (1, S::x) : f(S::x) (see below)
      2. or false (none of those expressions are discarded-value expressions)

It is unclear whether or not initialization applies the lvalue-to-rvalue conversion. One can argue that the "value of the lvalue-expression" must be read in order to initialize the int from an expression of type int const. If we follow this assumption, then the lvalue-to-rvalue conversion is applied to b ? (1, S::x) : f(S::X). The first occurrence of S::x is an element of the set of potential results of that expression (see the first part of this answer). Hence, Bullet point 3.0 of the above applies, and S::x is not odr-used through the first occurrence.

You can find a lot of information on lvalue-to-rvalue conversion in initializations in the Q&A Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB?. The situation might be a bit easier here, since the rhs has type int const. This might require a qualification conversion, which expects a prvalue operand (this probably invokes the lvalue-to-rvalue conversion implicitly).

Community
  • 1
  • 1
dyp
  • 38,334
  • 13
  • 112
  • 177
  • But how is that related to the fact that `S::x` is not odr-used in the second operand of the conditional operator, but is odr-used in its third operand? – Leon May 02 '15 at 01:28
  • 1
    @LeonTrotski I'm on it. Basically, the first occurrence is not odr-used, since it is "immediately" being read from, whereas the second occurrence has to bind to a reference. – dyp May 02 '15 at 01:40
  • That I can understand, but I'm having some difficulty establishing a relationship between your first answer and the second. – Leon May 02 '15 at 01:42
  • 1
    @LeonTrotski This is understandable. I'd like to make the connection by saying that the lvalue-to-rvalue conversion is applied to the whole expression `b ? (1, S::x) : f(S::x)`, which would establish a link. However, I'm not sure that this conversion really is applied to the initializer. – dyp May 02 '15 at 01:44
  • I'm trying hard to understand [basic.def.odr]/3. In C++11 this paragraph was written this way: "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 is **immediately** applied." What was, in C++11, the relationship between **not** being odr-used and an lvalue-to-rvalue converstion that is **immediately** applied? That is, what was the relevance of the word **immediately** in this paragraph? – Leon May 03 '15 at 21:58
  • @LeonTrotski I think the *immediately* was referring to something like `int const& f(int const& x) { return x; }` being used in an expression like `42 + f(S::x)`. The lvalue-to-rvalue conversion is applied to the right-hand-side of `+`, which *after evaluation* - but not immediately - refers to `S::x`. Also see [CWG DR 712](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712) – dyp May 03 '15 at 22:45
  • The example you gave or the example in DR 712 shows why [basic.def.odr]/3 was wrong in C++11 and had to be updated. In your example, the rule in C++11 would say that the variable `S::x` is not odr-used, but it is, since a reference is applied to it in function `f`. But that's not what I was asking in my prior comment. I think there must be a fundamental reason for the **immediately** in the paragraph, being associated with **not oder-used**, that some times fail, as in your example and in the example in DR712.( to be continued) – Leon May 03 '15 at 23:39
  • But the question is, what is this fundamental association between **an immediate lvalue-to-rvalue conversion** and **non odr-used**? – Leon May 03 '15 at 23:39
  • @LeonTrotski As fas as I understand the C++11 rules, `S::x` **is** odr-used in my example `42 + f(S::x)`, since the lvalue-to-rvalue conversion is **not immediately** applied to `S::x`. On the other hand, in `42 + S::x`, the lvalue-to-rvalue conversion is immediately applied to `S::x`, therefore `42 + S::x` does *not* odr-use `S::x`. -- Essentially, if the object needs to have an address for the expression to be (portably) evaluated, the variable is odr-used. If only the value is required (portably), then we don't need an object i.e. no odr-use. – dyp May 03 '15 at 23:50
  • Yes, I made a mistake on my comment regarding your example, although I would still say that the term **immediately** is irrelevant in this case, as there is **no** lvalue-to-rvalue conversion applied to the variable `S::x`in your example. Therefore, I'm still clueless about the relationship between this word (**immediately**) and the fact that a variable is **not** odr-used, as stated in C++11. – Leon May 04 '15 at 12:48
  • @LeonTrotski The lvalue-to-rvalue conversion in the expression `42 + f(S::x)` (if we accept that it is applied to both operands of the built-in operator `+`) is applied to the result of `f(S::x)`. This result, for the function `int const& f(int const& x) { return x; }` is an lvalue referring to `S::x`. Hence, the lvalue-to-rvalue conversion is applied to `S::x`, but not immediately - only after evaluation of `f`. – dyp May 04 '15 at 13:34
  • I finally came to terms with the fact that [basic.def.odr]/3 was wrong in C++11 thanks to you. Now I think I'm better equipped to tackle your answer for this paragraph in C++14 (+1). Many thanks. – Leon May 04 '15 at 17:40
  • @LeonTrotski I don't quite understand - what do you think is wrong in C++11's [basic.def.odr]/3? – dyp May 04 '15 at 18:53
  • Well, the example in DR 172 is typical. [basic.def.odr]/3 as written in C++11 says that the two variables `S::a`and `S::b` are odr-used, and they are not, notwithstanding the fact that these variables are **not** subjected to the lvalue-to-rvalue conversion! – Leon May 04 '15 at 21:34
  • @LeonTrotski DR 172 says that "this is surprising and unfortunate", not "this is wrong". In its example, `S::a` and `S::b` **are odr-used**, because the Standard specifies what odr-use is. Should they be odr-used? No, they shouldn't - there's no need to require a definition for those objects. So the specification has been changed to be more reasonable, or more intuitive. (This does require a bit more work from a compiler, I guess. So it could as well be seen as a relaxation of the former rules.) – dyp May 04 '15 at 22:03
  • [basic-def-odr]/3 mentions discarded-value expression. But according to [stmt.expr]/1 **every** *expression* is a discarded-value expression. What is then, the purpose of mentioning discarded-value expression in [basic-def-odr] : "..., where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression. – Leon May 06 '15 at 21:49
  • @LeonTrotski [stmt.expr]/1 says that the expression an *expression-statement* consists of is a discarded-value expression. Subexpression of the expression of an expression-statement, or expressions appearing outside of expression-statements can be non-discarded-value expressions. -- In other words, the value produced by the expression `e` in the statement `e;` is discarded. However, the value of the expression `v` in the statement `a = v;` is typically not discarded (but assigned to `a`), even though the value of the expression `a = v` is discarded. – dyp May 06 '15 at 21:54
  • Consider this very simple example: `const int i = 1; int f(int j) { return j;} int k = f(i);` Clearly the variable `i` is not odr-used by the expression `f(i)`. Nevertheless, applying [basic.def.odr]/3 we get that variable `i` is odr-used. If we apply the l-t-r conversion to `i` we get a constant value, but since the set of potential results of the expression `f(i)` is empty, we come to the conclusion that `i` is odr-used by `f(i)`. Where am I wrong here? – Leon May 11 '15 at 19:20
  • @LeonTrotski As I said in my answer, I expect this kind of initialization to cause l-t-r conversion, but cannot prove it. Therefore, I'd say that `f(i)` causes an l-t-r of `i` - that is, `i` is in the set of potential results of the expression `i` and the l-t-r is applied immediately to it (before evaluating `f`). – dyp May 11 '15 at 20:38
  • I believe you're wrong here. The `expression e` referred in the last part of [basic.def.odr]/3 is `f(i)`, not `i`. `i` is just a sub-expression of `e`. Let's see what the experts say here: https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rmJL-TzjlVY/mRKqFSAVpy8J – Leon May 12 '15 at 13:00
  • @dyp `extern S s; int i = s.x;` According to the steps, for `s`, 2.1 is false because `s` is not const, for `x`, 2 is not satisfied because `x` is not an element of the set of potential results of `s.x`. Can you add the steps of `s` and `x` in your answer? – stackcpp Jul 13 '15 at 09:54
  • @stackcpp Is this an entirely new example? Sorry, I think my answer here is already too long. Could you ask this as a separate (new) question? -- But I think your reasoning sounds about right. – dyp Jul 13 '15 at 10:07
  • @dyp see http://stackoverflow.com/questions/31381563/one-definition-rule-about-class-member-access-expression – stackcpp Jul 13 '15 at 10:59