C++1y/C++14: Converting static constexpr array to non-type template parameter pack?
Asked Answered
S

2

10

Suppose I have a constexpr array (of known bound) of static storage duration:

constexpr T input[] = /* ... */;

And I have an output class template that needs a pack:

template<T...> struct output_template;

I want to instantiate output_template like:

using output = output_template<input[0], input[1], ..., input[n-1]>;

One way to do this is:

template<size_t n, const T (&a)[n]>
struct make_output_template
{
    template<size_t... i> static constexpr
    output_template<a[i]...> f(std::index_sequence<i...>)
    { return {}; };

    using type = decltype(f(std::make_index_sequence<n>()));
};

using output = make_output_template<std::extent_v<decltype(input)>, input>::type;

Is there a cleaner or simpler solution I am missing?

Stereography answered 3/7, 2014 at 18:5 Comment(11)
what are you looking for? simpler interface or implementation?Hammerless
Could the indices trick be useful here? You don't have a template index parameter to play with here, as you would with a tuple, but...Clawson
using output = decltype(deduce(input))::unpack<input>;? Not much cleaner, though.Guano
@Guano linkage of a seems to be the hurdle (even with more constexpr in appropriate places): coliru.stacked-crooked.com/a/2c196c6f2a82cbfbHammerless
@Hammerless That's what clang says ;) But indeed, addresses used as non-type template arguments must refer to objects with linkage, IIRC due to name mangling. The rule what is allowed as an address argument is very strict, it doesn't even allow derived-to-base conversions.Guano
@TemplateRex: It's not that. It's just that you can't use a constexpr function parameter as a template argument, as it isn't a constant expression. constexpr int f(int a) { constexpr int b = a; return b; } // ill-formed.Stereography
@TemplateRex: A function call to a constexpr function (with constant arguments) is a constant expression, yes - however within the body of the contexpr function definition the parameters are not constant expressions.Stereography
It seems like this is another potential application of proposal n3601, which would allow using output = foo<input>; as far as I can see.Guano
@Guano I though of an easier syntax to improve N3601: template<auto V> where V is a compile-time value and the type can be easily accessed with decltype(V). When I asked Mike and Daveed (the authors of N3601) about it, it turns out they also already came up with the exact same idea and syntax and want to prepare an updated version of N3601 :)Homerhomere
@DanielFrey ... I think I've seen this somewhere before.. but I cannot remember where or find it. Edit: maybe https://mcmap.net/q/394276/-is-it-possible-to-emulate-template-lt-auto-x-gt or groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/…Guano
@Guano Looks like several people arrived at the same conclusion independently - which is IMHO a very good sign that this is the best syntax/solution :)Homerhomere
H
10

Maybe you consider this to be cleaner:

template< const T* a, typename >
struct make_output_template;

template< const T* a, std::size_t... i >
struct make_output_template< a, std::index_sequence< i... > >
{
    using type = output_template< a[ i ]... >;
};

with

using output = make_output_template<
    input,
    std::make_index_sequence< std::extent_v< decltype( input ) > >
>::type;
Homerhomere answered 3/7, 2014 at 23:51 Comment(3)
I do indeed sir, thanks. I guess all the examples of index_sequence I have seen use deduction of function parameters so I didn't think to use partial specicialization on the class.Stereography
There is no language specific feature that could stop me to try this using C++11, isn't?Bougie
@Bougie Yes, you are right, all it needs it C++11 once you replaced std::extend_v<...> with std::extend<...>::value and you rolled your own version of std::make_index_sequence.Homerhomere
S
0

C++ 17 update: With auto template parameters, we can make both OP's solution and Daniel Frey's answer a bit nicer to use. In OP's case:

template<auto& input>
using output = make_output_template<std::extent_v<std::remove_reference_t<decltype(input)>>, input>::type;

which can be used directly, e.g.: outupt<input> foo;.

Be sure to use auto& to capture the array correctly. With bare auto it will decay into a regular pointer (see e.g. https://stackoverflow.com/a/6443267), std::extent_v will be zero, but the code can still compile, and if you expect the variadic pack in output_template to be of the same size as the input array, you have a brand new bug.

Shorn answered 16/1, 2024 at 7:52 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.