Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuation of "I just can not understand DR 712"

This is basically a continuation of my prior question about DR 712. Let me first explain why I'm insisting in looking at something that could be considered old, as the C++11 Standard, but my problem is that the section [basic.def.odr] is already difficult to understand in C++11, and I want to cover this completely before I delve into the same section in the current draft, which in my opinion, is even more complicated.

The answer to my prior question by Austing Hastings was great, but I still have one little point that is not clear in [basic.def.odr]/2 in C++11. Consider this little and very simple example:

const int i = 1;
int main()
{
    int j = i;
}

From [basic.def.odr]/2 in C++11 i is not odr-used in int j = i; as i is an object that satisfies the requirement for appearing in a constant expression and the lvalue-to-rvalue conversion is immediately applied to i. That doesn't make much sense to me, as i is clealy being used in the declaration int j = i;, as can be seen in the slightly modified code shown here, where I forced the variable i not to be optimized out of the compiled code.

Of course, there must be something wrong in my reasoning above, for I don't believe C++11 could be wrong in such a simple example. Again, what am I missing now???

like image 337
João Afonso Avatar asked Mar 01 '17 14:03

João Afonso


1 Answers

I am trying to translate my understanding of standardese "used" and "one definition rule used" to something more intuitive here. Terms other than "used" and "ODR-used" are not intended as standard-defined terms below.


Something being ODR-used basically means "we need it to have an identity". This usually means someone is taking a reference or pointer to it.

If you only need the value of something, this doesn't always make it ODR-used. The value of a compile-time constant does not require identity.

In C++, identity basically means "it must actually have storage somewhere".

The standard doesn't say "it is ODR used if we need it to have an identity", because then different compilers will have different rules to determine if they need the identity. As an example, if an operation is inlined and the reference elided, does that mean it no longer needs an identity?

So the standard describes what ODR-used means, and distinguishes it from the value being used.

int j = i;

this doesn't need the identity of i. It just needs its value. const int i = 1; has a value that cannnot (under defined behavior) change.

int const* pj = &i;

this does need an identity. Two different pointers to i must agree on the location of i under the standard.

void foo( const int& x ) {
  int j = x;
}
foo(i);

This also needs an identity for i. We take a reference to i. Even though the only thing we do is just that reference its value, the (short, theoretical) existence of that reference means it had an identity.

const int a = 3; const int b = 4;
int i = (a<2)?a:b;

the defect there was that this required a and b to have identity (they where ODR-used), because the the interaction of ? with the used-rules. The defect was saying "we should fix this".

Link to DR 712

And after that resolution, only the value of a and b are required by that expression, not their identity, so they don't need to have storage.

We care if something has storage because things requiring storage have to have a unique point of definition. They cannot exist purely in a header file, basically.

Note that with inline variables in C++17 we may care less about this; under the as-if rule, the inline storage location created can be erased from existence if nobody actually pays attention to the identity. As functions taking const& can "accidentally" force identity requirements on things intended to be value-only tokens, this is a nice relaxing of the rules.

like image 112
Yakk - Adam Nevraumont Avatar answered Oct 22 '22 01:10

Yakk - Adam Nevraumont