I have a code like
template <size_t N>
class A
{
template <size_t N>
someFunctions() {};
};
Now I want to create instances of the class and call the functions in it in a for loop for a set of many values like
// in main()
int main()
{
for (int i = 1; i <= 100; i++)
{
const int N = i; // dont know how to do this
A<N> a;
a.functionCalls();
}
}
How to do this? Hoping for a method to do this.
This would require something called a template for
which is the expected form expansion statements will take, which is something that look like a for loop but in reality is a templated block in a function that is instanciated multiple times.
Of course, there is a workaround. We can abuse generic lambdas to declare some sort of local templated block and instanciate it ourself:
template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F f) {
(static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}
This function takes an integer sequence and instantiate the lambda F
as many time as the length of the sequence.
It is used like this:
for_sequence(std::make_index_sequence<100>(), [](auto N) { /* N is from 0 to 99 */
A<N + 1> a; /* N + 1 is from 1 to 100 */
a.functionCalls();
});
Here, N
can be sent as template parameter because it's an object that has a constexpr conversion operator to an integer type. More precisely, it's a std::integral_constant
with an increasing value.
Live example
The N
needs to be compile-time constant, which is with a normal for
loop is not possible.
But, there are many workarounds. For instance, inspired by this SO post, you can do something like the following. (See a Live demo)
template<size_t N>
class A
{
public:
// make the member function public so that you can call with its instance
void someFunctions()
{
std::cout << N << "\n";
};
};
template<int N> struct AGenerator
{
static void generate()
{
AGenerator<N - 1>::generate();
A<N> a;
a.someFunctions();
}
};
template<> struct AGenerator<1>
{
static void generate()
{
A<1> a;
a.someFunctions();
}
};
int main()
{
// call the static member for constructing 100 A objects
AGenerator<100>::generate();
}
Prints 1
to 100
In c++17, the above can be reduced to a single template AGenerator
class(i.e. specialization can be avoided), using if constexpr
. (See a Live demo)
template<std::size_t N>
struct AGenerator final
{
static constexpr void generate() noexcept
{
if constexpr (N == 1)
{
A<N> a;
a.someFunctions();
// .. do something more with `a`
}
else
{
AGenerator<N - 1>::generate();
A<N> a;
a.someFunctions();
// .. do something more with `a`
}
}
};
Output:
1
2
3
4
5
6
7
8
9
10
In case of providing the range of iteration, you could use the following.(See a Live demo)
template<std::size_t MAX, std::size_t MIN = 1> // `MIN` is set to 1 by default
struct AGenerator final
{
static constexpr void generate() noexcept
{
if constexpr (MIN == 1)
{
A<MIN> a;
a.someFunctions();
// .. do something more with `a`
AGenerator<MAX, MIN + 1>::generate();
}
else if constexpr (MIN != 1 && MIN <= MAX)
{
A<MIN> a;
a.someFunctions();
// .. do something more with `a`
AGenerator<MAX, MIN + 1>::generate();
}
}
};
int main()
{
// provide the `MAX` count of looping. `MIN` is set to 1 by default
AGenerator<10>::generate();
}
Outputs the same as the above version.
From C++20, you can use template lambdas, so you can try something as follows
[]<int ... Is>(std::integer_sequence<int, Is...>)
{ (A<Is+1>{}.functionCall(), ...); }
(std::make_integer_sequence<int, 100>{});
The following is a full compiling example that print all numbers from 0 to 99
#include <utility>
#include <iostream>
int main()
{
[]<int ... Is>(std::integer_sequence<int, Is...>)
{ (std::cout << Is << std::endl, ...); }
(std::make_integer_sequence<int, 100>{});
}
One way you can do this is with template meta-programming with something like this:
#include <iostream>
template <std::size_t N>
struct A {
void foo() { std::cout << N << '\n'; }
};
template <std::size_t from, std::size_t to>
struct call_foo {
void operator()() {
if constexpr (from != to) {
A<from + 1>{}.foo();
call_foo<from + 1, to>{}();
}
}
};
int main() { call_foo<0, 100>{}(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With