Why is S::x not odr-used?

C++Language LawyerC++17

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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

Solution 2 - C++

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;

Solution 3 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionb1subView Question on Stackoverflow
Solution 1 - C++T.C.View Answer on Stackoverflow
Solution 2 - C++YSCView Answer on Stackoverflow
Solution 3 - C++SHRView Answer on Stackoverflow