Reinterpret_cast creating a trivial default construct

cppreference & dagger; declares that:

Objects with trivial default constructors can be created using reinterpret_cast on any suitable aligned repository, for example. to memory allocated using std::malloc .

This means that the following code is correct:

 struct X { int x; }; alignas(X) char buffer[sizeof(X)]; // (A) reinterpret_cast<X*>(buffer)->x = 42; // (B) 

The following three questions:

  • Is this a quote right?
  • If so, at what point does X lifetime begin? If in line (B) , is it cast, which is considered a memory acquisition? If in line (A) , if there was a branch between (A) and (B) , which conditionally built X or some other module, Y ?
  • Is something changing between C ++ 11 and C ++ 1z in this regard?



& dagger; Please note that this is an old link. The wording has been changed in response to this question. Now it reads:

However, unlike C, objects with trivial default constructors cannot be created by simply reinterpreting the appropriate storage alignment, such as the memory allocated with std::malloc : place-new is required to formally introduce a new object and prevent potential undefined behavior .

+50
c ++ language-lawyer c ++ 1z c ++ 11 c ++ 17
Nov 29 '16 at 18:48
source share
2 answers

There is no X object, living or otherwise, so pretending to be one result in undefined behavior.

[intro.object] / 1 sets out exhaustively when creating objects:

An object is created by definition ([basic.def]), by new-expression ([expr.new]) when an implicit change is an active member of a union ([class.union]), or when a temporary object ([conv.rval], [class .temporary]).

With the adoption of P0137R1, this paragraph is the definition of the term "object".

Is there a definition for object X ? No. Is there a new expression? No. Is there a union? No. Does your language have a language construct that creates a temporary object X ? No.

Regardless of what [basic.life] says about the lifetime of an object with empty initialization, it does not matter. To do this, you need to have the object first. You do not do this.

C ++ 11 has roughly the same paragraph, but does not use it as a definition of "object." However, the interpretation remains the same. An alternative interpretation - processing [basic.life] as creating an object, as soon as a suitable repository - means that you create Schrödinger objects * which contradicts N3337 [intro.object] / 6 :

Two objects that are not bit fields can have the same address if one is a subobject of the other, or if at least one of them is a base class, a subobject of zero size and they are of different types; otherwise, they must have different addresses.




* Storage with proper alignment and size for type T is a storage, by definition, with proper alignment and size for every other type, the size and alignment requirements of which are equal to or less than T Thus, this interpretation means that the simultaneous receipt of the repository creates an infinite set of objects with different types in the said repository having the same address.

+31
Nov 29 '16 at 19:29
source share

This analysis is based on n4567 and uses the partition numbers from it.

§5.2.10 / 7: When a prvalue v type of an object pointer is converted to an object pointer type of "pointer to cv T", the result is static_cast<cv T*>(static_cast<cv void*>(v)) .

So, in this case, reinterpret_cast<X*>(buffer) same as static_cast<X *>(static_cast<void *>(buffer)) . This leads us to consider the relevant parts about static_cast :

§5.2.9 / 13: prvalue of type "pointer to cv1 void" can be converted to prvalue of type "pointer to cv2 T", where T is the type of object and cv2 is the same cv qualification as or more cv qualifications than cv1. The null pointer value is converted to the null pointer value for the destination type. If the initial value of the pointer represents the address A byte in memory, and A satisfies the alignment requirement T , then the obtained value of the pointer represents the same address as the original value of the pointer, that is, A

I believe that it is enough to say that the original quote is correct - this conversion gives certain results.

As for the lifetime, it depends on what kind of life you are talking about. The cast creates a new object of the type of the pointer - temporary, which has a lifespan starting from the line in which the throw is located and ends when it goes beyond the bounds. If you have two different transformations that occur conditionally, each pointer has a lifespan that begins with the location of the throw it created.

None of them affects the lifetime of the object that provides the underlying storage, which is still buffer , and has exactly the same lifetime, regardless of whether you create a pointer (of the same or converted type) for this storage or not.

+3
Nov 29 '16 at 19:15
source share



All Articles