Doc. no.: N3824
Date: 2013-10-22
Project: Programming Language C++, Library Evolution Working Group
Reply-to: Zhihao Yuan <zy at miator dot net>

make_array

Motivation

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.

Scope

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.

Examples

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.

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.

Wording

This wording is relative to N3797, which contains the resolution of LWG 2141.

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... Types>
  constexpr see below make_array(Types&&...);
template <class D, class... Types>
  constexpr see below make_array(Types&&...);
template <class T, size_t N>
  constexpr see below to_array(T (&a)[N]);

}

New section 23.3.2.9 [array.creation] (between [array.zero] and [array.tuple], which was 23.3.2.9):

23.3.2.9 Array creation functions [array.creation]

template <class... Types>
  constexpr array<CT, sizeof...(Types)> make_array(Types&&...);

Let Ui be remove_reference<Ti>::type for each Ti in Types.

Remarks: This function shall not participate in overload resolution unless each Ui is not reference_wrapper<Ti>.

[Just a note: This one is banned for genericity reason, so use SFINAE to allow users to handle them in generic code. –end note]

Returns: An array<CT, sizeof...(Types)> initialized with { std::forward<Types>(t))... }, where CT is common_type<Types...>::type.

[Example:

    int i = 1; int& ri = i;
    make_array(i, ri, 42L)

creates an array of type

    array<long, 3>

–end example]

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

Returns: An array<D, sizeof...(Types)> initialized with { std::forward<Types>(t))... }.

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

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

[Just a note: The remove_cv here functionally performs decay, while intentionally kills constructing from multidimensional array with a hard error, because std::array is not aware of multidimensional array (yet), and I don’t want user to try anything may silently break their code in the future. On the other hand, if you understand multidimensional std::array as array of arrays, it might be more convenient and clear to write

make_array(make_array(1, 2, 3), make_array(4, 5, 6)...)

instead of to adapt a raw array. –end note]

Sample Implementation

A sample implementation is available at https://gist.github.com/lichray/6034753.

Acknowledgments

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.