Doc. no.: N4391
Date: 2015-02-26
Project: Programming Language C++, Library Working Group
Revises: N4315
Reply-to: Zhihao Yuan <zy at miator dot net>

make_array, revision 4

Changes since N4315

Changes since N4065

Changes since N4031

Changes since N3824


We have make_tuple, make_pair, but not make_array, while std::array creation can also benefit from this “semiofficial” tuple-like interface to deduce both element type and array bound.


LWG 851 intended to provide a replacement syntax to

array<T, N> a = { E1, E2, ... };

, so the following

auto a = make_array(42u, 3.14);

is well-formed (with additional static_casts applied inside) because

array<double, 2> = { 42u, 3.14 };

is well-formed.

This paper intends to provide a set of std::array creation interfaces which are comprehensive from both tuple’s point of view and array’s point of view, so narrowing is just naturally banned. See more details driven by this direction in Design Decisions.


auto a1 = make_array(2, 3L);        // array<long, 2>
auto ax = make_array(2, 3U);        // error: narrowing

auto a2 = make_array<long>(2, 3U);      // explicit destination type
auto ax = make_array<unsigned>(2, 3U);  // error: narrowing

auto a3 = make_array("foo");        // array<char const*, 1>, decayed
auto a4 = to_array("foo");          // array<char, 4>

Design Decisions

make_array(ref(a), ref(b))

also results in a tuple-like object storing T&. However, std::array does not store “real” references, and any attempts to workaround this break the interfaces in different ways. Note that “doing nothing” is not an option since, for example, common_type_t<reference_wrapper<int>, reference_wrapper<long>> is long, not reference or reference_wrapper.

make_array("raw array")  // got array<char const*, 1>

is inexplicable. However, to keep the interfaces consistent, I decide to name a new utility differently instead of to ban this conversion.


This wording is relative to N4296.

Add to 23.3.1/2 [sequences.general], <array> synopsis:

namespace std {
  template <class T, size_t N > struct array;

  template <class T, size_t N >
    void swap(array<T,N>& x, array<T,N>& y) noexcept(noexcept(x.swap(y)));
template <class D = void, class... Types>
  constexpr array<VT, sizeof...(Types)> make_array(Types&&...);
template <class T, size_t N>
  constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);


New section [array.creation] (between [] and [array.tuple], which was Array creation functions [array.creation]

template <class D = void, class... Types>
  constexpr array<VT, sizeof...(Types)> make_array(Types&&...);

Let Ui be decay_t<Ti> for each Ti in Types.

Remarks: The program is ill-formed if D is void and at least one Ui is a specialization of reference_wrapper.

Returns: array<VT, sizeof...(Types)>{ std::forward<Types>(t)... }, where VT is common_type_t<Types...> if D is void, otherwise VT is D.


    int i = 1; int& ri = i;
    auto a1 = make_array(i, ri);         // a1 is of type array<int, 2>
    auto a2 = make_array(i, ri, 42L);    // a2 is of type array<long, 3>
    auto a3 = make_array<long>(i, ri);   // a3 is of type array<long, 2>
    auto a4 = make_array<long>();        // a4 is of type array<long, 0>
    auto a5 = make_array();              // ill-formed

–end example]

[Editorial note: The non-code text in the example above, such as “is of type”, “ill-formed”, should be in italic face. ]

template <class T, size_t N>
  constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);

Returns: An array<remove_cv_t<T>, N> such that each element is copy-initialized with the corresponding element of a.

Sample Implementation

A sample implementation is available at


Jonathan Wakely, who showed me how index_sequence helps initializing std::array from a raw array.

Daniel Kr├╝gler, who explained why an explicit destination type is essential.

Ville Voutilainen and other people who reviewed this paper.

Stephan T. Lavavej, who pointed out the ambiguity issue of the two make_array overloads.