Date: Fri, 15 Dec 2023 13:55:41 -0800
I would like to semi-formally propose a defect report related to std::array
and existing standards.
It turns out std::array<T, 0> cannot be implemented in C++17 without the
use of magic compiler builtins. libc++, libstdc++, and I think also MSVC
are all non-compliant.
Begin and end are required to return a unique pointer, the only constexpr
method I have found to do this is taking the address of a union member.
However, that approach breaks if T is non-trivial.
Glibc libstdc++ libc++ ignores the standard requirement of uniqueness in
favor of following the trivial and constexpr requirements. Both of these
implementations return nullptr.
Second, libc++ didn't use a single empty aggregate (this might force an ABI
break), not sure if that one was adopted in the standard, but that should
not be included in a standard. glibc libstdc++ follows this requirement,
but it's an ABI break for libc++.
There are, as I see it, three possible solutions:
1. Remove the "uniqueness" requirement from std::array<T, 0>::begin and
::end, (only in the case where N=0) and drop the empty aggregate
requirement from new versions of the standard. This would make libstdc++
and libc++'s implementation returning nullptr standard compliant by
changing the standard. Least disruptive solution.
2. Extend the "is_trivial" logic to allow initializers that
initialize empty aggregates to not make the class be considered
non-trivial, e.g.
struct empty {};
struct foo { empty e = {}; }
Here, `foo` could become considered a trivial type.
However, this might break existing code that relies on is_trivial, so I
think that is a bad solution for existing C++ versions. Though I doubt any
code would actually break? This might be a good idea to explore for C++26
though as I don't see any downside to making this trivial aside from any
rare corner case of a library that depends on this non-triviality.
3. Drop the deleted union destructors for non-trivial types. I like this
solution a lot but there is some code breakage possibility.
plus the bonus non-solution,
4. implement some compiler builtin that can be used as a weird hack to
implement the std::array<T, 0> standardized behavior and get every compiler
that wants to use libc++ to implement it.
and existing standards.
It turns out std::array<T, 0> cannot be implemented in C++17 without the
use of magic compiler builtins. libc++, libstdc++, and I think also MSVC
are all non-compliant.
Begin and end are required to return a unique pointer, the only constexpr
method I have found to do this is taking the address of a union member.
However, that approach breaks if T is non-trivial.
Glibc libstdc++ libc++ ignores the standard requirement of uniqueness in
favor of following the trivial and constexpr requirements. Both of these
implementations return nullptr.
Second, libc++ didn't use a single empty aggregate (this might force an ABI
break), not sure if that one was adopted in the standard, but that should
not be included in a standard. glibc libstdc++ follows this requirement,
but it's an ABI break for libc++.
There are, as I see it, three possible solutions:
1. Remove the "uniqueness" requirement from std::array<T, 0>::begin and
::end, (only in the case where N=0) and drop the empty aggregate
requirement from new versions of the standard. This would make libstdc++
and libc++'s implementation returning nullptr standard compliant by
changing the standard. Least disruptive solution.
2. Extend the "is_trivial" logic to allow initializers that
initialize empty aggregates to not make the class be considered
non-trivial, e.g.
struct empty {};
struct foo { empty e = {}; }
Here, `foo` could become considered a trivial type.
However, this might break existing code that relies on is_trivial, so I
think that is a bad solution for existing C++ versions. Though I doubt any
code would actually break? This might be a good idea to explore for C++26
though as I don't see any downside to making this trivial aside from any
rare corner case of a library that depends on this non-triviality.
3. Drop the deleted union destructors for non-trivial types. I like this
solution a lot but there is some code breakage possibility.
plus the bonus non-solution,
4. implement some compiler builtin that can be used as a weird hack to
implement the std::array<T, 0> standardized behavior and get every compiler
that wants to use libc++ to implement it.
Received on 2023-12-15 21:55:54