Search code examples
c++c++17initializer-list

How to create std::initializer_list with integer_sequence?


I have an integer_sequence and I need to create std::initializer_list for constructor of std::map. The idea is below:

template<uint8_t... Indices>
static constexpr auto GenerateRegMap(std::integer_sequence<uint8_t, Indices...>)
    -> std::initializer_list<std::pair<uint8_t const, uint8_t>>
{
    return {{Indices, 0}...};
}

constexpr auto s = std::integer_sequence<uint8_t, 2, 4, 5, 7, 8, 10, 11, 13, 15>{};
std::map<uint8_t, uint8_t> m (GenerateRegMap(s));
std::cout << m.size() << std::endl;
for (const auto &pair : m)
{
    std::cout << static_cast<int>(pair.first) << " has value " << static_cast<int>(pair.second) << std::endl;
}

There std::initializer_list is normally generated, but it cannot be returned: it uses local storage. I cannot imagine how to write the content of GenerateRegMap in place.

Really m is private field of some class and it would be nice to init it in the member initializer list.

Why don't I return std::map<uint8_t, uint8_t> from GenerateRegMap? Because I've got an error: a constexpr function cannot have a nonliteral return type.


Solution

  • Instead of returning a local initializer_list from the GenerateRegMap, you can use C++14 variable template to generate the initializer_list directly

    template<uint8_t... Indices>
    constexpr std::initializer_list<std::pair<uint8_t const, uint8_t>> my_list = 
      {{Indices, 0}...};
    
    constexpr auto s = my_list<2, 4, 5, 7, 8, 10, 11, 13, 15>;
    
    std::map<uint8_t, uint8_t> m(s);
    

    If you want the variable template to take integer_sequence as the template type, you can use template partial specialization to get values of integer_sequence to initialize initializer_list, for example

    using L = std::initializer_list<std::pair<uint8_t const, uint8_t>>;
    template<class Seq>
    constexpr L my_list = {};
    template<class Seq>
    constexpr L my_list<const Seq> = my_list<Seq>;
    template<uint8_t... Indices>
    constexpr L my_list<std::integer_sequence<uint8_t, Indices...>> = {{Indices, 0}...};
    

    This enables you to generate initializer_list with pre-defined integer_sequence

    constexpr auto s = std::integer_sequence<uint8_t, 2, 4, 5, 7, 8, 10, 11, 13, 15>{};
    std::map<uint8_t, uint8_t> m(my_list<decltype(s)>);