C++ Logo

std-proposals

Advanced search

Re: Making bit_cast Useful

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Thu, 24 Sep 2020 10:12:55 -0400
On Thu, Sep 24, 2020 at 9:39 AM connor horman via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Thu, 24 Sep 2020 at 08:51, Andrew Giese via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> * If r is an expression of type T, the expression std::bit_cast<T>(r), if
>>> well-formed, shall be equivalent to copy constructing the result from r,
>>> except that the expression treats copy constructors which are defined as
>>> deleted as though they were defined as default instead, and does not call
>>> move constructors even if r is an rvalue.
>>
>>
>> std::bit_cast only works for types that are TriviallyCopyable, and will
>> emit a compiler error otherwise. This "Reflexive" case seems perhaps
>> unnecessary then.
>>
>> It was added for completeness. It can also be used when the copy
> constructor is defined as deleted, because TriviallyCopyable allows any of
> the copy constructor, move constructor, copy assignment, and move
> assignment to be deleted IIRC, as long as at least one is not. Though,
> you'd be right, I should mention that it's only the case if the expression
> is well-formed. I've changed it to that effect.
>

FWIW, "does not call move constructors" is also redundant, since T is
trivially copyable. For a trivially copyable type, there's no difference
between "calling" the trivial move constructor, "calling" the trivial copy
constructor, or just copying the bits.

reference_wrapper:
>>> * If r is an expression of type std::reference_wrapper<T>and U is
>>> (possibly differently cv-qualified) T, then the expression
>>> std::bit_cast<U*>(r), shall be well-formed and an equivalent expression to
>>> const_cast<U*>(std::addressof(static_cast<T&>(r)))
>>> * If r is an expression of type T*, which points to an object, and U is
>>> (possibly differently cv-qualified) T, then the expression
>>> std::bit_cast<std::reference_wrapper<U>>(r) shall be well-formed, and an
>>> equivalent expression to std::ref(*const_cast<U*>(r)). If r does not point
>>> to an object, the behaviour is undefined. If T is an incomplete type other
>>> than (possibly cv-qualified) void or an array of unknown bound, the
>>> expression treats T as though it was completed. If T is an array of unknown
>>> bound, the expression treats T as though it were an array with length 1.
>>>
>>
This series of requirements seems novel. Is your intent to mandate that
std::reference_wrapper<T> should be implemented as a standard-layout class
type with one member of type T* stored at offset zero, and no other
members, and no padding? Is this already mandated somewhere in C++20, or
is this a novelty?

AFAIK all vendors' implementations do implement reference_wrapper this way,
but if you're adding requirements on the implementation, the right place
for those is probably in the section on reference_wrapper, not in the
section on bit_cast.


> Unresolved Questions:
>>> std::optional<std::reference_wrapper<T>> is currently not treated
>>> separately. It may be nice to apply a similar pair of rules as for regular
>>> std::reference_wrapper, with the additional clause that an empty optional
>>> bit_casts to a null pointer and the reverse. However, unlike the
>>> reference_wrapper requirements which are effective in libstdc++ and libc++
>>> (because its implemented with a single raw pointer member),
>>> std::optional<std::reference_wrapper<T>> is not implemented as such (though
>>> libc++ interestingly implements the storage for std::optional<T&> in case
>>> the standard ever decides to permit it, and this is done simply using a raw
>>> pointer). A possibility is not enforcing well-formedness, but keeping the
>>> behaviour if it is well-formed.
>>>
>>
Right, you can't mandate that sizeof(optional<reference_wrapper<T>>) ==
sizeof(T*) unless you're willing to take an ABI break, which no vendor is.
For more on the possibilities of "tombstone representations" in
std::optional, see my C++Now 2018 presentation "The Best Type Traits C++
Doesn't Have." <https://www.youtube.com/watch?v=MWBfmmg8-Yo>


The reference_wrapper rules don't allow void*->reference_wrapper<void>. Is
>>> there a reason to allow this? Can reference_wrapper<void> exist?
>>>
>>
Formally, no; in [refwrap]/1, reference_wrapper<T> implicitly requires that
it be possible to have "an object or function of type T," which is not the
case when T=void.
http://eel.is/c++draft/refwrap#general-1
Also, there is no special wording to eliminate
`reference_wrapper<T>::operator T&() const` when T=void, which again
implies that instantiating reference_wrapper<void> would make your program
ill-formed.

–Arthur

Received on 2020-09-24 09:13:08