851. simplified array construction

Section: 24.3.7 [array] Status: NAD Submitter: Benjamin Kosnik Opened: 2008-06-05 Last modified: 2017-06-06 14:03:11 UTC

Priority: Not Prioritized

View all other issues in [array].

View all issues with NAD status.


This is an issue that came up on the libstdc++ list, where a discrepancy between "C" arrays and C++0x's std::array was pointed out.

In "C," this array usage is possible:

int ar[] = {1, 4, 6};

But for C++,

std::array<int> a = { 1, 4, 6 }; // error

Instead, the second parameter of the array template must be explicit, like so:

std::array<int, 3> a = { 1, 4, 6 };

Doug Gregor proposes the following solution, that assumes generalized initializer lists.

template<typename T, typename... Args>
inline array<T, sizeof...(Args)> 
make_array(Args&&... args) 
{ return { std::forward<Args>(args)... };  }

Then, the way to build an array from a list of unknown size is:

auto a = make_array<T>(1, 4, 6);

[ San Francisco: ]

Benjamin: Move to Ready?

Bjarne: I'm not convinced this is useful enough to add, so I'd like us to have time to reflect on it.

Alisdair: the constraints are wrong, they should be

template<ValueType T, ValueType... Args>
requires Convertible<Args, T>...
array<T, sizeof...(Args)> make_array(Args&&... args);

Alidair: this would be useful if we had a constexpr version.

Bjarne: this is probably useful for arrays with a small number of elements, but it's not clearly useful otherwise.

Consensus is to move to Open.

[ 2009-06-07 Daniel adds: ]

I suggest a fix and a simplification of the current proposal: Recent prototyping by Howard showed, that a fix is required because narrowing conversion 9.4.5 [dcl.init.list]/6 b.3 would severely limit the possible distribution of argument types, e.g. the expression make_array<double>(1, 2.0) is ill-formed, because the narrowing happens inside the function body where no constant expressions exist anymore. Furthermore given e.g.

int f();
double g();

we probably want to support

make_array<double>(f(), g());

as well. To make this feasible, the currently suggested expansion

{ std::forward<Args>(args)... }

needs to be replaced by

{ static_cast<T>(std::forward<Args>(args))... }

which is safe, because we already ensure convertibility via the element-wise Convertible<Args, T> requirement. Some other fixes are necessary: The ValueType requirement for the function parameters is invalid, because all lvalue arguments will deduce to an lvalue-reference, thereby no longer satisfying this requirement.

The suggested simplification is to provide a default-computed effective type for the result array based on common_type and decay, in unconstrained form:

template<typename... Args>
array<typename decay<typename common_type<Args...>::type>::type,
make_array(Args&&... args);

The approach used below is similar to that of make_pair and make_tuple using a symbol C to represent the decayed common type [Note: Special handling of reference_wrapper types is intentionally not provided, because our target has so satisfy ValueType, thus under the revised proposal only an all-reference_wrapper-arguments would be well-formed and an array of reference_wrapper will be constructed]. I do currently not suggest to add new concepts reflecting decay and common_type, but an implementor will need something like this to succeed. Note that we use a similar fuzziness for make_pair and make_tuple currently. This fuzziness is not related to the currently missing Constructible<Vi, Ti&&> requirement for those functions. The following proposal fixes that miss for make_array. If the corresponding C type deduction is explicitly wanted for standardization, here the implementation

auto concept DC<typename... T> {
  typename type = typename decay<typename common_type<T...>::type>::type;

where C is identical to DC<Args...>::type in the proposed resolution below.

I intentionally added no further type relation between type and the concept template parameters, but instead added this requirement below to make the specification as transparent as possible. As written this concept is satisfied, if the corresponding associated type exists.

Suggested Resolution:

  1. Add to the array synopsis in 24.3 [sequences]:

    template<ReferentType... Args>
    requires ValueType<C> && IdentityOf<Args> && Constructible<C, Args&&>...
    array<C, sizeof...(Args)>
    make_array(Args&&... args);
  2. Append after [array.tuple] Tuple interface to class template array the following new section: Array creation functions [array.creation]

    template<ReferentType... Args>
    requires ValueType<C> && IdentityOf<Args> && Constructible<C, Args&&>...
    array<C, sizeof...(Args)>
    make_array(Args&&... args);

    Let C be decay<common_type<Args...>::type>::type.

    Returns: an array<C, sizeof...(Args)> initialized with { static_cast<C>(std::forward<Args>(args))... }.

[ 2009-07 Frankfurt: ]

The proposed resolution uses concepts.

Daniel to rewrite the proposed resolution.

Leave Open.

[ 2009-07-25 Daniel provides rewritten proposed resolution. ]

[ 2009-10 Santa Cruz: ]

Argument for NAD future: everything about this could be added on. This does not require changes to the existing text.

[2015-11-29, Alisdair comments]

N4391 was adopted for Fundamentals 2 at the Lenexa meeting.

[2017-02 in Kona, LEWG recommends NAD]

[2017-06-02 Issues Telecon]

It seems as if deduction guides can solve most of the problem. In addition to that, make_array is available in Library Fundamentals TS v2. If it's desired to be able to specify the type but not the extent, make_array can do that, and if make_array isn't acceptable for that, we are talking about a language extension in deduction guides, which needs a proposal paper.

Resolve as NAD

Proposed resolution:

  1. Add to the array synopsis in 24.3 [sequences]:

    template<class... Args>
      array<CT, sizeof...(Args)>
      make_array(Args&&... args);
  2. Append after [array.tuple] "Tuple interface to class template array" the following new section:

    XX.X.X.X Array creation functions [array.creation]

    template<class... Args>
    array<CT, sizeof...(Args)>
    make_array(Args&&... args)

    Let CT be decay<common_type<Args...>::type>::type.

    Returns: An array<CT, sizeof...(Args)> initialized with { static_cast<CT>(std::forward<Args>(args))... }.


    int i = 0; int& ri = i;
    make_array(42u, i, 2.78, ri);

    returns an array of type

    array<double, 4>

    end example]