Date: Thu, 24 Sep 2020 08:12:28 -0400
C++20 added the function std::bit_cast to the standard library. While the
result of it is defined, simply reinterpreting the bytes making up the
object-representation, there are actually no defined results (beyond the
identity case, which is simply copy-constructing the result from the
input). This seeks to add limitations on the results of std::bit_cast,
which are believed to be reasonable, and may be expected in practice.
Reflexive:
(Included for completeness)
* If r is an expression of type T, the expression std::bit_cast<T>(r) 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.
Enumerations and Integral types:
* If r is an expression of a (potentially cv-qualified) integral type other
than bool or an enumeration type, and U is a (potentially cv-qualified)
integral type other than bool or an enumeration type, the expression
std::bit_cast<U>(r), if well formed, shall be an equivalent expression to
static_cast<U>(r)
Pointer types:
* If r is an expression of type (potentially cv-qualified) pointer to void
or pointer to a character type, and U such a type, let P be the same
pointer type as the type or r, but with the same cv-qualifiers (ignoring
top-level cv-qualifiers) as U, the expression std::bit_cast<U>(r) shall be
well-formed and shall be an equivalent expression as
reinterpret_cast<U>(const_cast<P>(r))
* If r is an expression of type T*, and U is the same type as T ignoring
top level cv-qualifiers, then the expression std::bit_cast<U>(r) shall be
well-formed, and shall be an equivalent expression to const_cast<U*>(r)
Pointer-interconvertible:
* If r is an (potentially const-qualified) lvalue, designating an object a,
and U is a type, such that (ignoring all top-level cv-qualifiers), there is
an object of type U, b, that is *pointer-interconvertible* with a, then the
expression std::bit_cast<U>(r), if well formed, is equivalent to
copy-constructing the result from a const-qualified lvalue designating b.
Union:
* If r is an expression of type T, and U is a union type which has at least
one member of type T which is not a bitfield, if the expression
std::bit_cast<U>(r), if well-formed, shall yield a union where the active
member is the member of type T and is initialized as though by
std::bit_cast<T>(r). If there are multiple such members, the active member
is the first such member accessed by the program, or otherwise an
unspecified member of that type. If the union has a member of a reference
type, the behaviour is undefined.
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.
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.
These rules allow you to construct union values (provided the union is the
same size as the member). Would it be reasonable to also allow the
construction of Trivially Copyable, Standard Layout structure types with a
single member of the source type?
None of these rules cover transitive bit casts. Would it be reasonable to
require such over all of these rules, or just a limited case (for example,
void*->reference_wrapper<char>)?
The reference_wrapper rules don't allow void*->reference_wrapper<void>. Is
there a reason to allow this? Can reference_wrapper<void> exist?
result of it is defined, simply reinterpreting the bytes making up the
object-representation, there are actually no defined results (beyond the
identity case, which is simply copy-constructing the result from the
input). This seeks to add limitations on the results of std::bit_cast,
which are believed to be reasonable, and may be expected in practice.
Reflexive:
(Included for completeness)
* If r is an expression of type T, the expression std::bit_cast<T>(r) 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.
Enumerations and Integral types:
* If r is an expression of a (potentially cv-qualified) integral type other
than bool or an enumeration type, and U is a (potentially cv-qualified)
integral type other than bool or an enumeration type, the expression
std::bit_cast<U>(r), if well formed, shall be an equivalent expression to
static_cast<U>(r)
Pointer types:
* If r is an expression of type (potentially cv-qualified) pointer to void
or pointer to a character type, and U such a type, let P be the same
pointer type as the type or r, but with the same cv-qualifiers (ignoring
top-level cv-qualifiers) as U, the expression std::bit_cast<U>(r) shall be
well-formed and shall be an equivalent expression as
reinterpret_cast<U>(const_cast<P>(r))
* If r is an expression of type T*, and U is the same type as T ignoring
top level cv-qualifiers, then the expression std::bit_cast<U>(r) shall be
well-formed, and shall be an equivalent expression to const_cast<U*>(r)
Pointer-interconvertible:
* If r is an (potentially const-qualified) lvalue, designating an object a,
and U is a type, such that (ignoring all top-level cv-qualifiers), there is
an object of type U, b, that is *pointer-interconvertible* with a, then the
expression std::bit_cast<U>(r), if well formed, is equivalent to
copy-constructing the result from a const-qualified lvalue designating b.
Union:
* If r is an expression of type T, and U is a union type which has at least
one member of type T which is not a bitfield, if the expression
std::bit_cast<U>(r), if well-formed, shall yield a union where the active
member is the member of type T and is initialized as though by
std::bit_cast<T>(r). If there are multiple such members, the active member
is the first such member accessed by the program, or otherwise an
unspecified member of that type. If the union has a member of a reference
type, the behaviour is undefined.
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.
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.
These rules allow you to construct union values (provided the union is the
same size as the member). Would it be reasonable to also allow the
construction of Trivially Copyable, Standard Layout structure types with a
single member of the source type?
None of these rules cover transitive bit casts. Would it be reasonable to
require such over all of these rules, or just a limited case (for example,
void*->reference_wrapper<char>)?
The reference_wrapper rules don't allow void*->reference_wrapper<void>. Is
there a reason to allow this? Can reference_wrapper<void> exist?
Received on 2020-09-24 07:12:41