Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expanding a constexpr array into a set of non-type template parameters

Suppose I have a compile-time constexpr array and a variadic class template with a set of non-type parameters of the same type as the elements of the array.

My objective is to instantiate the class template with the values from the array:

struct Container
{
    int containee[3];
};

constexpr Container makeContainer();

template <int... Elements> class Foo;

Foo<makeContainer().containee[0],
    makeContainer().containee[1],
    makeContainer().containee[2]> foo;

The above code works well. However, I'm quite unhappy about having to manually index the array whenever I need to instantiate the Foo template. I would like the compiler to do that for me automatically:

Foo<Magic(makeContainer().containee)> foo;

I did some RTFM at cppreference, but that didn't help. I'm aware of std::forward<>(), but it cannot be applied to template argument lists.

like image 264
Pavel Kirienko Avatar asked Nov 13 '17 11:11

Pavel Kirienko


1 Answers

  1. Change makeContainer to a struct with a constexpr operator() or a constexpr lambda (C++17). A function pointer will not work here.

    struct makeContainer
    {
        constexpr auto operator()() const
        {
            return Container{/* ... */};
        }
    };
    
  2. Use std::make_index_sequence and std::index_sequence to generate a compile-time sequence of the indices:

    template <typename C>
    constexpr auto fooFromContainer(const C& container)
    {
        return fooFromContainerImpl(container, std::make_index_sequence<3>{});
    }
    
  3. Create a new constexpr container instance through C, then expand the sequence to index the elements in a constant expression:

    template <typename C, std::size_t... Is>
    constexpr auto fooFromContainerImpl(const C& container, std::index_sequence<Is...>)
    {
        constexpr auto c = container();
        return Foo<c.containee[Is]...>{};
    }
    

complete example on wandbox.org


Just for fun, here's a C++20 implementation:

struct container { int _data[3]; };

template <int... Is> 
struct foo 
{ 
    constexpr auto t() const { return std::tuple{Is...}; }
};

template <typename C>
constexpr auto foo_from_container(const C& c)
{
    return []<std::size_t... Is>(const auto& c, std::index_sequence<Is...>)
    {
        return foo<c()._data[Is]...>{};
    }(c, std::make_index_sequence<3>{});
}

int main()
{
    constexpr auto r = foo_from_container([]{ return container{42, 43, 44}; });   
    static_assert(r.t() == std::tuple{42, 43, 44});
}

live example on wandbox.org

like image 178
Vittorio Romeo Avatar answered Oct 01 '22 00:10

Vittorio Romeo