C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Only reason I don't use std::array

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Thu, 17 Aug 2023 21:08:08 +0100
On Thu, 2023-08-17 at 16:40 +0100, Frederick Virchanza Gotham via Std-
Proposals wrote:
> On Thu, Aug 17, 2023 at 12:41 PM Ville Voutilainen via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > Well, yeah - the actual problem is that nothing prevents the
> > implementation from putting a T[N+IMPL_DEFINED_CONSTANT] there.
>
>
> I would feel safe using the following code on anything from an
> Arduino
> microcontroller to an x86_64 Ubuntu PC. I don't anticipate a problem
> with it, especially because of the two static_assert's. Do you think
> it could possibly malfunction on even the strangest of platforms?
>
> GodBolt: https://godbolt.org/z/c14YjqPvK
>
> #include <cstddef> // size_t
> #include <array> // array
> #include <type_traits> // is_const, remove_const
>
> template< typename T, std::size_t len > requires
> (!std::is_const_v<T>)
> std::array<T,len> &pretend_is_std_array( T (&arg)[len] )
> {
> static_assert( sizeof(std::array<T,len>) == sizeof(T[len]) );
>
> return *static_cast< std::array<T,len>*
> >(static_cast<void*>(&arg));
> }
>
> template< typename T, std::size_t len > requires std::is_const_v<T>
> std::array< std::remove_const_t<T>, len > const
> &pretend_is_std_array(
> T (&arg)[len] )
> {
> static_assert( sizeof(std::array< std::remove_const_t<T>, len >)
> == sizeof(T[len]) );
>
> return *static_cast< std::array< std::remove_const_t<T>, len >
> const * >(static_cast<void const*>(&arg));
> }
>
> void SomeFunc ( std::array<int,16> & ) {}
> void SomeFuncC( std::array<int,16> const & ) {}
>
> int main(void)
> {
> int monkey[16u] = {};
> int const donkey[16u] = {};
>
> SomeFunc ( pretend_is_std_array(monkey) );
> SomeFuncC( pretend_is_std_array(donkey) );
> }

The way this UB can manifest in interesting ways is something like:

struct A {
  int arr[1];
};

void foo(A& a, std::array<int, 1>& b) {
  a.arr[0] += b[0];
  a.arr[0] += b[0];
  // a and b can't alias, this can be optimised to
  // a.arr[0] += 2*b[0];
}

int main() {
  A a{{1}};
  foo(a, pretend_is_std_array(a.arr)); // breaking the aliasing
assumption here
  return a.arr[0];
  // may return either 3 or 4 depending on how foo is optimized
}

In general aliasing analysis is not a theoretical concern. It's not
something that some future sufficiently smart compiler will use for
optimisation. It is used for optimisation right now.

Cheers,
Lénárd

Received on 2023-08-17 20:08:12