Posting new consts and links breaks?

After discussing my answer to this question , apparently:

the following is allowed

struct Foo { int x; }; Foo f; Foo & f_ref = f; (&f) -> ~Foo (); new (&f) Foo (); int x = f_ref .x; 

but the following code is not allowed

 struct Foo { const int & x; // difference is const reference Foo (int & i) : x(i) {} }; int i; Foo f (i); Foo & f_ref = f; (&f) -> ~Foo (); new (&f) Foo (i); int x = f_ref .x; 

Over $ 3.8 / 7

If after the life of the object has expired and before the repository that the object is busy reused or released, a new object is created in the storage location where the original object was loaded, a pointer pointing to the original object, a link related to the original object, or the name of the source object will automatically refer to the new object and, as soon as the lifetime of the new object is started, it can be used to manage the new object if:

  • the type of the source object is not const-quali fiified, and if the class type does not contain a non-static data element whose type is const-qualified or a reference type ..

I can understand how a link to fx can be invalidated if f ceases to exist, but I don’t understand why f_ref should be invalidated just because one of its members is a constant and / or link, and not otherwise: it was link to Foo before and after that refers to Foo .

Can someone explain the reason for this condition?

Edit

Thanks for answers. I do not buy the argument “guarantee does not change”, because we do not allow optimizers to cache referees, for example:

 struct Foo { const int & x; Foo (const int & i) : x(i) {} void do_it (); }; int i; Foo f (i); const int & ii = fx; f .do_it (); // may modify i std :: cout << ii; // May NOT use cached i 

I don’t see how do_it allowed to cancel reference values, but operator new is not - Sequence points have invalid cached values : why should / placement -new be freed?

+7
source share
4 answers

I believe that the motivation is to allow the compiler to cache the values ​​of const objects (note that const objects, not just pointers to pointers to a constant and a link to a constant) and the addresses of referee links to calls to unknown code.

In your second example, the compiler can “first see” that the object was created and destroyed, and secondly, it was recreated using the same value. But the authors of the standard wanted compilers to be allowed to include this code:

 struct Foo { const int & x; Foo (int & i) : x(i) {} }; int i = 1; Foo f(i); some_function_in_another_TU(&f); std::cout << fx; 

In it:

 struct Foo { const int & x; Foo (int & i) : x(i) {} }; int i = 1; Foo f(i); some_function_in_another_TU(&f); std::cout << i; // this line is optimized 

since the reference element f cannot be reinstalled, and therefore it must still refer to i . The destruct-and-construct operation violates the non-recoverable x reference element.

This optimization should not be particularly controversial: consider the following example using a const object, not an object with a const or reference element:

 const int i = 1; some_function_in_another_TU(&i); std::cout << i; 

Here i is a compile-time constant, some_function_in_another_TU cannot correctly destroy it and create another value instead of it int . Therefore, the compiler should be allowed to emit code for std::cout << 1; . The idea is that the same should be true by analogy for const objects of other types and for references.

If calling an unknown code can reinstall the reference element or change the value of the const data element, then a useful language invariant (links will never be re-closed, and constant objects will never change their values) will be violated.

+7
source

As far as I can tell, this is just a matter of semantic correctness and the underlying assumptions that the optimizer can make. Consider this:

 Bar important, relevant; Foo x(important); // binds as const-reference Zoo z(x); // also binds as const reference do_stuff(z); x.~Foo(); ::new (&x) Foo(relevant); // Ouch? 

The z object can reasonably expect its reference to Foo be constant, and therefore refer to important . As the standard says, destruction plus a new construction in the last two lines “automatically updates all links to refer to a (logically) new object”, so now the const constant inside z has changed, despite the promise to be constant.

To avoid this conflicting const-correctness violation, all on-site reconstruction is prohibited.

+4
source

Optimization. Suppose I have:

 struct Foo { int const x; Foo( int init ) : x( init ) {} }; int main() { Foo a( 42 ); std::cout << ax << std::endl; new (&a) Foo( 3 ); std::cout << ax << std::endl; return 0; } 

The compiler, upon seeing the const int object, has the right to assume that the value does not change; the optimizer can simply store the value in the register at the new placement and display it again.

Please note that your example is actually quite different. A data member is of type int const& ; it is a link (and links are always const), so the compiler can assume that the link always refers to the same object. (The value of this object can change, if only the object itself is also const.) The compiler cannot make such an assumption about the value of the object to which it refers, however, since i (in your case) can be clearly changed. It is a fact that the link (like all links) is immutable, which causes undefined behavior here, and not the const that you wrote.

+2
source

If something is const-qualified, you should not modify it. Life extension is a modification with serious consequences. (For example, consider whether the destructor has side effects.)

-one
source

All Articles