Search code examples
c++arraysinitializationaggregate-initializationctad

Initialize std::array of certain type, but any size?


Let's say I have the following code:

using Element = std::array<int, 2>;

constexpr auto arrayOfElement()
{
    return std::array
    {
        Element{ 1, 2 },
        Element{ 2, 3 },
        Element{ 123, 123 },
        Element{ 8008, 1337 }
    };
}

This compiles just fine, but in order to indicate that the type I meant as the element of the returned array, I had to constantly repeat the Element type name. Written in another way:

constexpr auto arrayOfElement2()
{
    return std::array<Element, 4>
    {
        Element{ 1, 2 },
        { 2, 3 },
        { 123, 123 },
        { 8008, 1337 }
    };
}

Here I don't have to type Element as many times as there are elements, but in this case I cannot have the size deduced, and the first element has to be typed, which makes the whole thing look kind of awkward by having an edge case for the first element.

Is there a way in which I can specify the Element type only once, but also have the size of the array deduced (partial CTAD)?


Solution

  • Since you can't specify the element type without also specifying the number of elements, use std::to_array:

    constexpr auto arrayOfElement() {
        return std::to_array<Element>({
            { 1, 2 },
            { 2, 3 },
            { 123, 123 },
            { 8008, 1337 }
        });
    }
    

    From the notes on the cppreference wiki page:

    There are some occasions where class template argument deduction of std::array cannot be used while to_array is available:

    • to_array can be used when the element type of the std::array is manually specified and the length is deduced, which is preferable when implicit conversion is wanted.
    • to_array can copy a string literal, while class template argument deduction constructs a std::array of a single pointer to its first character.