This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3436. `std::construct_at` should support arrays

Section: 27.11.8 [specialized.construct] Status: New Submitter: Jonathan Wakely Opened: 2020-04-29 Last modified: 2021-12-11

Priority: 2

View all issues with New status.

Discussion:

`std::construct_at` is ill-formed for array types, because the type of the `new`-expression is `T` not `T*` so it cannot be converted to the return type.

In C++17 `allocator_traits::construct` did work for arrays, because it returns `void` so there is no ill-formed conversion. On the other hand, in C++17 `allocator_traits::destroy` didn't work for arrays, because `p->~T()` isn't valid.

In C++20 `allocator_traits::destroy` does work, because `std::destroy_at` treats arrays specially, but `allocator_traits::construct` no longer works because it uses `std::construct_at`.

It seems unnecessary and/or confusing to remove support for arrays in `construct` when we're adding it in `destroy`.

I suggest that `std::construct_at` should also handle arrays. It might be reasonable to restrict that support to the case where `sizeof...(Args) == 0`, if supporting parenthesized aggregate-initialization is not desirable in `std::construct_at`.

[2020-05-09; Reflector prioritization]

Set priority to 2 after reflector discussions.

[2021-01-16; Zhihao Yuan provides wording]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4878.

> 1. Modify 27.11.8 [specialized.construct] as indicated:

>    >     template<class T, class... Args>
>    >       constexpr T* construct_at(T* location, Args&&... args);

>    >     namespace ranges {
>    >       template<class T, class... Args>
>    >         constexpr T* construct_at(T* location, Args&&... args);
>    >     }

>    > > -1- Constraints: The expression `::new (declval<void*>()) T(declval<Args>()...)` is well-formed when treated as an unevaluated operand.

>    > > -2- Effects: Equivalent to:

>    > > >     returnauto ptr = ::new (voidify(*location)) T(std::forward<Args>(args)...);
>    > > >     if constexpr (is_array_v<T>)
>    > > >       return launder(location);
>    > > >     else
>    > > >       return ptr;

[2021-12-07; Zhihao Yuan comments and provides improved wording]

The previous PR allows constructing arbitrary number of elements when `T` is an array of unknown bound:

>     extern int a[];
>     std::construct_at(&a, 0, 1, 2);

and leads to a UB.

Proposed resolution:

This wording is relative to N4901.

1. Modify 27.11.8 [specialized.construct] as indicated:

   >     template<class T, class... Args>
   >       constexpr T* construct_at(T* location, Args&&... args);

   >     namespace ranges {
   >       template<class T, class... Args>
   >         constexpr T* construct_at(T* location, Args&&... args);
   >     }

   > > -1- Constraints: The expression `::new (declval<void*>()) T(declval<Args>()...)` is well-formed when treated as an unevaluated operand (7.2.3 [expr.context]) and `is_unbounded_array_v<T>` is `false`.

   > > -2- Effects: Equivalent to:

   > > >     returnauto ptr = ::new (voidify(*location)) T(std::forward<Args>(args)...);
   > > >     if constexpr (is_array_v<T>)
   > > >       return launder(location);
   > > >     else
   > > >       return ptr;