Date: Sun, 6 Aug 2023 15:02:58 +0100
Just want to clarify this clearly to be 100% sure.
> Not in general; [basic.lval] para. 11 is specifically about reading
> and writing scalar values through a glvalue, and nothing else. Since
> ptr2 is a T* pointer pointing to an std::byte object, you can't do
> most things with ptr2 that you could do with an arbitrary T* pointer,
ptr2 should point to T2 now
https://eel.is/c++draft/expr#reinterpret.cast-7
https://eel.is/c++draft/expr#reinterpret.cast-3
Not sure how strict aliasing (https://eel.is/c++draft/expr#basic.lval-11)
can be violated in the example below, as the access (for scalar variables)
would be done through dynamic type, as T2 lifetime has started and
*storage_* provides storage for it.
T2* ptr2 = reinterpret_cast<T2 *>(&*storage_*)
ptr2->value/*scalar*/;
> ([expr.ref] para. 8). Most (but not all) usual
> operations require that the dynamic type of the pointee actually
> matches the type of the pointer.
I don't see how the above violates the sample below.
reinterpret_cast<T2 *>(&storage_)->....
The type of expression above is T* and that is what the *actual* result of
reintepret_cast is as T2's object address should be equal to the
address of *storage_
*and storage_ *provides storage* for T2's object which is in turn tested
within *storage_*. Although I agree that std::launder would make it more
explicit and I can be wrong on this.
https://eel.is/c++draft/basic#intro.object-9
On Sun, Aug 6, 2023 at 3:05 AM Matthew House via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> On Sat, Aug 5, 2023 at 8:30 PM Mykola Garkusha via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> > This is still an inconclusive answer, which is the central question on
> this thread really.
> >
> > alignas(T) std::byte storage_[sizeof(T)];
> > T *ptr_ = ::new (&storage_) T;
> > T* ptr2 = reinterpret_cast<T *>(&storage_)
> >
> > Is using ptr2 well-defined just on the grounds that according to the
> aliasing rules accessing objects through std::byte, unsigned char or the
> DnamicType is well-defined?
>
> Not in general; [basic.lval] para. 11 is specifically about reading
> and writing scalar values through a glvalue, and nothing else. Since
> ptr2 is a T* pointer pointing to an std::byte object, you can't do
> most things with ptr2 that you could do with an arbitrary T* pointer,
> e.g., you can't access its members with a ptr->field class member
> access expression ([expr.ref] para. 8). Most (but not all) usual
> operations require that the dynamic type of the pointee actually
> matches the type of the pointer.
>
> > On the contrary, the following is not well-defined and would need
> std::launder to provide the correct behavior given the size and alignment
> for T1 and T2 match.?
> >
> > T1 storage;
> > T2 *ptr_ = ::new (&storage_) T2;
> > T2* ptr2 = reinterpret_cast<T2 *>(&storage_) // UB?
> > T2* ptr2 = std::launder(reinterpret_cast<T2 *>(&storage_)) //
> well-defined?
> > ptr->~T2();
> > ::new (&storage_) T1; //needed if T1 got non-trivial destructor
>
> Yes, std::launder() is needed to have well-defined behavior in this
> example.
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
> Not in general; [basic.lval] para. 11 is specifically about reading
> and writing scalar values through a glvalue, and nothing else. Since
> ptr2 is a T* pointer pointing to an std::byte object, you can't do
> most things with ptr2 that you could do with an arbitrary T* pointer,
ptr2 should point to T2 now
https://eel.is/c++draft/expr#reinterpret.cast-7
https://eel.is/c++draft/expr#reinterpret.cast-3
Not sure how strict aliasing (https://eel.is/c++draft/expr#basic.lval-11)
can be violated in the example below, as the access (for scalar variables)
would be done through dynamic type, as T2 lifetime has started and
*storage_* provides storage for it.
T2* ptr2 = reinterpret_cast<T2 *>(&*storage_*)
ptr2->value/*scalar*/;
> ([expr.ref] para. 8). Most (but not all) usual
> operations require that the dynamic type of the pointee actually
> matches the type of the pointer.
I don't see how the above violates the sample below.
reinterpret_cast<T2 *>(&storage_)->....
The type of expression above is T* and that is what the *actual* result of
reintepret_cast is as T2's object address should be equal to the
address of *storage_
*and storage_ *provides storage* for T2's object which is in turn tested
within *storage_*. Although I agree that std::launder would make it more
explicit and I can be wrong on this.
https://eel.is/c++draft/basic#intro.object-9
On Sun, Aug 6, 2023 at 3:05 AM Matthew House via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> On Sat, Aug 5, 2023 at 8:30 PM Mykola Garkusha via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> > This is still an inconclusive answer, which is the central question on
> this thread really.
> >
> > alignas(T) std::byte storage_[sizeof(T)];
> > T *ptr_ = ::new (&storage_) T;
> > T* ptr2 = reinterpret_cast<T *>(&storage_)
> >
> > Is using ptr2 well-defined just on the grounds that according to the
> aliasing rules accessing objects through std::byte, unsigned char or the
> DnamicType is well-defined?
>
> Not in general; [basic.lval] para. 11 is specifically about reading
> and writing scalar values through a glvalue, and nothing else. Since
> ptr2 is a T* pointer pointing to an std::byte object, you can't do
> most things with ptr2 that you could do with an arbitrary T* pointer,
> e.g., you can't access its members with a ptr->field class member
> access expression ([expr.ref] para. 8). Most (but not all) usual
> operations require that the dynamic type of the pointee actually
> matches the type of the pointer.
>
> > On the contrary, the following is not well-defined and would need
> std::launder to provide the correct behavior given the size and alignment
> for T1 and T2 match.?
> >
> > T1 storage;
> > T2 *ptr_ = ::new (&storage_) T2;
> > T2* ptr2 = reinterpret_cast<T2 *>(&storage_) // UB?
> > T2* ptr2 = std::launder(reinterpret_cast<T2 *>(&storage_)) //
> well-defined?
> > ptr->~T2();
> > ::new (&storage_) T1; //needed if T1 got non-trivial destructor
>
> Yes, std::launder() is needed to have well-defined behavior in this
> example.
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
Received on 2023-08-06 14:03:13