52

Consider this example from cppreference:

struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x

I agree that &S::x is a discarded-value expression, since the standard says (9.2, paragraph 1 [stmt.expr] from n4700)

Expression statements have the form

expression-statement:
    expression_opt ;

The expression is a discarded-value expression (Clause 8)...

However, is that enough for S::x to not be odr-used? 6.2, paragraph 3 [basic.def.odr] states

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless

  • ...
  • 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 (7.1) is applied to e, or
    • e is a discarded-value expression (Clause 8).

The problem is that the discarded-value expression &S::x has no potential results (which means that S::x is not a potential result of &S::x), as you can see from 6.2, paragraph 2 [basic.def.odr]:

... The set of potential results of an expression e is defined as follows:

  • If e is an id-expression (8.1.4), the set contains only e.
  • If e is a subscripting operation (8.2.1) with an array operand, the set contains the potential results of that operand.
  • ...
  • Otherwise, the set is empty.

Then, how can you explain that S::x is not odr-used?

  • 3
    Another problem with the wording of the standard is that `S::x` is not a *name* . It's a qualified-id. But apparently the sentence "A variable x whose name..." is also supposed to apply to `x` being a qualified-id. – M.M Dec 23 '17 at 14:50
  • 4
    Yep, that's a bug on our end. Fixed. – T.C. Dec 23 '17 at 21:56
  • @M.M something like this? https://github.com/cplusplus/draft/pull/1996 – Ryan Haining Mar 28 '18 at 20:20
  • @T.C. What's the rational to make the discarded-value expression `&S::x` odr-use `x`? – xskxzr May 31 '18 at 06:40
  • 1
    @xskxzr Taking the address of something generally requires said thing to exist. What's the benefit of carving out an exception for this case? – T.C. Jun 01 '18 at 05:07
  • I'm not the expert in this, but i think you missed one condition in 6.2: A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression. 6.2.3: [...] discarded-value expression. The lvalue-to-rvalue conversion (7.1) is applied if and only if the expression is a glvalue of volatile-qualified type and it is one of the following: (12.6) — pointer-to-member operation (8.5), – Zacharias Sep 25 '18 at 10:06
  • in https://en.cppreference.com/w/cpp/language/definition under "ODR-use" they say "Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written,...". So if it is a compile-time constant it is not odr-used. Not sure why in your case it considers it as compile-time constant though maybe compiler bug or standard doesnt draw clear line for const and constexpr for basic types. First thing to do is to check if "unless it is a compile time constant" is true then check if your S::x is compile time constant – Abdurrahim Dec 07 '18 at 17:49
  • @Zacharias I'm not sure what you're getting at. The example above clearly was a bug on cppreference, and already been fixed at the time of writing this. – eca2ed291a2f572f66f4a5fcf57511 Dec 07 '18 at 18:53

3 Answers3

5

It is indeed odr-used. Your analysis is correct (and I fixed that example a while ago).

T.C.
  • 133,968
  • 17
  • 288
  • 421
-1

Yes, in the example, &S::x odr-uses S::x.

[basic.def.odr]/4

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.

The address of an object is never a constant expression. That's why S::x is odr-used in &S::x.

To justify that last assertion:

[expr.const]/6

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints [...]

and

[expr.const]/2.7

2) An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
2.7) an lvalue-to-rvalue conversion unless it is applied to

(none of the following points applies:)

2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
2.7.2) a non-volatile glvalue that refers to a subobject of a string literal, or
2.7.3) a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
2.7.4) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

YSC
  • 38,212
  • 9
  • 96
  • 149
  • `&S::x` isn't an lvalue (so it can't undergo lvalue-to-rvalue conversion), and `&` doesn't cause `S::x` to undergo lvalue-to-rvalue conversion, so why are you quoting rules for lvalue-to-rvalue conversions? – Ben Voigt Feb 19 '19 at 14:48
-3

When declaring const int, it may be entirely discarded by the compiler unless you using its address. taking the address is not enough.

Discarded is not mean the value is not evaluated, it is, but it mean there is no memory address containing the const value, the compiler just replace the const variable by its value, as it was just a macro.

In addition, when taking a pointer to it and taking back the value from the pointer, doesn't impress the compiler much, it just ignores it and use the value.

The following code shows it, this code can be compiled and run (I test it by several compilers, I'm still not sure if it successfully compiled by all...) despite of the fact that S::x was not declared:

#include <iostream>
using namespace std;
struct S
{
    static const int x=0; 
};
//const int S::x; //discarded
int main()
{
    const int *px = &S::x;  //taking the address
    cout<< *px <<endl; //print the value - OK
    return 0;
}

But if I'll try to use the address itself(not the value) like:

cout<< px <<endl; //print the address - Will be failed

the link will failed dou to: "undefined reference to S::x".

Therefore, my conclusion is: taking an address without using it, doesn't count at all.

SHR
  • 7,940
  • 9
  • 38
  • 57
  • 11
    The question was tagged [tag:language-lawyer], that means OP wants the answer backed by references to the ISO C++ Standard. It is irrelevant that it compiles (in fact a lot of ODR violations compile just fine). – rustyx Jun 11 '18 at 15:04