62

How can I make a3 compile?

int main()
{
    int a1[] = { 1, 2, 3 };
    std::array<int, 3> a2 = { 1, 2, 3 };
    std::array<int> a3 = { 1, 2, 3 };
}

It's very inconvenient, and brittle, to hard-code the size of the array when using an initialization list, especially long ones. Is there any work around? I hope so otherwise I'm disappointed because I hate C arrays and std::array is supposed to be their replacement.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • 4
    You can implement your own `make_array()` function. – David G Oct 14 '14 at 02:06
  • 1
    Thank you, that is helpful, I might use it. But when such a horribly complex (to me) function is required to do something C could do 30 years, it ago strikes me as wrong somehow. Furthermore I'm concerned my compiler won't be smart enough to compile it to exactly the same as putting the size in myself. – Neil Kirk Oct 14 '14 at 02:12
  • 7
    C++17 introduced [deduction guides](http://en.cppreference.com/w/cpp/container/array/deduction_guides) for this kind of things, but you have to omit the template arguments. – bit2shift Oct 26 '17 at 16:02
  • In case someone is looking for a way to initialize a modifiable buffer that is initialized with a string literal without explicitly specifying its size, feel free to check out my answer here: https://stackoverflow.com/a/69507748/7107236 – 303 Oct 13 '21 at 09:30

2 Answers2

31

There is currently no way to do this without rolling your own make_array, there is a proposal for this N3824: make_array which has the following 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.

It also includes a sample implementation, which is rather long so copying here is impractical but Konrad Rudolph has a simplified version here which is consistent with the sample implementation above:

template <typename... T>
constexpr auto make_array(T&&... values) ->
    std::array<
       typename std::decay<
           typename std::common_type<T...>::type>::type,
       sizeof...(T)> {
    return std::array<
        typename std::decay<
            typename std::common_type<T...>::type>::type,
        sizeof...(T)>{std::forward<T>(values)...};
}
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Yes but can you use that to store `std::vector>`? Making a vector of `std::array` would require all the sizes to be the same? – Brandon Oct 14 '14 at 02:15
  • 1
    @Brandon Unfortunately I think they would need to be the same size. Forget my suggestion in the other thread :( – Neil Kirk Oct 14 '14 at 02:18
  • @Brandon `std::vector >` AFAIK is impossible. – GingerPlusPlus Oct 14 '14 at 12:18
  • Is there any reason return type deduction can't be used in that last sample or just `return {std::forward(values)...}`? – David G Oct 14 '14 at 16:17
  • Would @Konrad Rudolph's simplified version of `make_array()` above be better than the Intermediate version (in updated original question) posted [here](http://stackoverflow.com/q/6114067/1998099) ? What are the pro's and cons of each ? – DarkMatter Oct 20 '14 at 17:18
  • The final example is very nice, except that I don't understand why it repeats the `std::array<>` type when declaring it via `auto ->` enables us simply to `return { the_initializer_list }` without repeating the typename. (Or this certainly compiles fine for me, anyway., just like normal) – underscore_d Jan 14 '16 at 00:57
20

You're being a little overdramatic when you say "such a horribly complex (to me) function is required". You can make a simplified version yourself, the proposal also includes a "to_array" function to convert C-arrays and deducing the type from the first parameter. If you leave that out it gets quite manageable.

template<typename T, typename... N>
auto my_make_array(N&&... args) -> std::array<T,sizeof...(args)>
{
    return {std::forward<N>(args)...};
}

which you can then call like

auto arr = my_make_array<int>(1,2,3,4,5);

edit: I should mention that there actually is a version of that in the proposal that I overlooked, so this should be more correct than my version:

template <typename V, typename... T>
constexpr auto array_of(T&&... t)
    -> std::array < V, sizeof...(T) >
{
    return {{ std::forward<T>(t)... }};
}
PeterT
  • 7,981
  • 1
  • 26
  • 34
  • Yeah, wow, the 2nd example works perfectly for most cases and isn't complex at all. Thanks! – underscore_d Jan 14 '16 at 00:53
  • The only difference between the two proposed implementations is the extra set of curly braces? What syntactic difference does the _seemingly unnecessary_ pair of braces do? – Violet Giraffe May 11 '18 at 09:11
  • 4
    To add to these comments (albeit very late), the double braces are related to std::array initialization. [This is described here.](https://stackoverflow.com/a/11735045/560774) I think the reason why `{std::forward(args)...};` works is the slight relaxation of the rules for eliding braces, [described here](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3526.html). – dv_ Sep 04 '18 at 14:02