If I wanted to invoke instances of a function template where the template arguments are sequential integers, I know a loop like the following won't work:
template <int n>
void foo() {/*...*/}
for (int i = 0; i < 4; ++i)
foo<i>();
And same with classes.
Judging by the information I've been able to find, there are no tricks to get around this. But I haven't seen anyone trying to use std::iota
, and it's constexpr
since C++20.
2024 standard says:
13.4.3 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type template-parameter shall be a converted constant expression (7.7) of the type of the template-parameter.
And I couldn't really understand this 7.7 part.
But as I understand constexpr
, it should be known at compile time (like I know what I'm talking about). So maybe something like iterating through std::vector
of needed indexes filled with iota could work.
If I wanted to invoke instances of a function template where the template arguments are sequential integers [..]?
From your requirements, I would rather suggest a solution using std::integer_sequence
with range and fold expression expansion. For example, if you have:
// Example function template
template <int N> constexpr void foo()
{
std::cout << "foo<" << N << "> called\n";
}
// Example class template
template <int N> struct Bar
{
constexpr void print() const
{
std::cout << "Bar<" << N << "> instance\n";
}
};
you might create helper functions for each cases as follows (also possible to combine with if constexpr
):
// Helper for function templates with compile-time range
template <int Start, int End, typename Func>
constexpr void do_loop(Func)
{
// For each element in the sequence, call foo<Is + Start>()
[] <typename T, T... Is>(std::integer_sequence<T, Is...>) {
(foo<Is + Start>(), ...);
}(std::make_integer_sequence<int, End - Start>{});
}
// Generic helper for member function calls over a compile‐time range.
// Instead of accepting a pointer‐to‐member function, we accept a callable F,
// which (given an instance of C<...>) performs the desired call.
template <template <int> class C, int Start, int End, typename F>
constexpr void do_loop(F f)
{
// Generate an integer sequence and for each index, create an instance and call f on it.
[f] <typename U, U... Is>(std::integer_sequence<U, Is...>) {
((f(C<Is + Start>{})), ...);
}(std::make_integer_sequence<int, End - Start>{});
}
You would call it like:
do_loop<3, 6>(foo<0>);
do_loop<Bar, 0, 3>([](auto&& obj) { obj.print(); });
Output:
foo<3> called
foo<4> called
foo<5> called
Bar<0> instance
Bar<1> instance
Bar<2> instance