C++ Logo

STD-PROPOSALS

Advanced search

Subject: Re: [std-proposals] P0593R5 Implicit creation of objects ...: About reinterpret_cast in examples
From: Kazutoshi Satoda (k_satoda_at_[hidden])
Date: 2019-10-16 13:00:17


(Since the CC you sent to std-proposals did not appear on
list <https://lists.isocpp.org/std-proposals/2019/10/date.php>,
now I'm sending with your attachment. Maybe the list accepts
posts only from subscribers.)

On 2019/10/16 6:32 +0900, Richard Smith wrote:
> On Mon, 14 Oct 2019 at 09:16, Kazutoshi Satoda <k_satoda_at_[hidden]>
> wrote:
>
>> from P0593R5 <http://wg21.link/p0593r5>:
>>
>> #include <cstdlib>
>> struct X { int a, b; };
>> X *make_x() {
>> // The call to std::malloc implicitly creates an object of type X
>> // and its subobjects a and b in order to give the subsequent
>> // class member access operations defined behavior.
>> X *p = (X*)std::malloc(sizeof(struct X));
>> p->a = 1;
>> p->b = 2;
>> return p;
>> }
>>
>> AFAIK (since C++17), std::launder() is required to make the access
>> defined, unless the return value of malloc is said to point to the
>> created object or something pointer-interconvertible to it. I can't find
>> such a wording in proposed wordings.
...
> Yes, you're right. This had been pointed out before, but I forgot about it
> when revising the paper. Thank you for the reminder. Please find attached a
> draft of R6 of this paper that addresses this issue (as well as a couple of
> other issues people have raised since R5 was published).

Thank you for providing the update. It does answer my question.
Then, here are some more questions from the same viewpoint.

(from d0593r6.html)
On the second example in 3.7 "Practical examples";

  unique_ptr<char[]> Stream::read() {
    // ... determine data size ...
    unique_ptr<char[]> buffer(new char[N]);
    // ... copy data into buffer ...
    return buffer;
  }
  
  void process(Stream *stream) {
    unique_ptr<char[]> buffer = stream->read();
    if (buffer[0] == FOO)
      process_foo(reinterpret_cast<Foo*>(buffer.get())); // #1
    else
      process_bar(reinterpret_cast<Bar*>(buffer.get())); // #2
  }

followed by:
> Note the new char[N] implicitly creates objects within the allocated
> array. In this case, the program would have defined behavior if an
> object of type Foo or Bar (as appropriate for the content of the
> incoming data) were implicitly created prior to Stream::read populating
> its buffer.

How the first element of char array, to which "buffer.get()" points, is
pointer-interconvertible with an implicitly created object of type
Foo or Bar? AFAIK, there is no pointer-interconvertible relationship
regarding "provides storage" or "nested within".

On 5.11 "20.10.x Explicit lifetime management [obj.lifetime]":
> template<typename T> T *std::start_lifetime_as(void *p);
...
> Requires: [p, (char*)p + sizeof(T)) denotes a region of allocated
> storage that is a subset of the region of storage reachable through p.

Is this pointer arithmetic OK?

> Effects: Implicitly creates objects within the denoted region, including
> an object A of type T whose address is p. ...
...
> Returns: A pointer to A.
...
> template<typename T> T *std::start_lifetime_as_array(void *p, size_t n);
...
> Effects: Equivalent to: return start_lifetime_as<U>(p); where U is the
> type "array of n T".

I think it should be "return &(*start_lifetime_as<U>(p))[0]" or
"return *start_lifetime_as<U>(p)" (relying array-to-pointer conversion),
and require n > 0.

-- 
k_satoda



STD-PROPOSALS list run by std-proposals-owner@lists.isocpp.org

Standard Proposals Archives on Google Groups