In some contexts, it could be useful/necessary to have a for loop evaluated/unrolled at compile time. For example, to iterate over the elements of a tuple
- Is there a way to write compile_time_for such that it accepts a template function as its first argument?
Short answer: no.
Long answer: a template function isn't an object, is a collection of objects and you can pass to a function, as an argument, an object, non a collection of objects.
The usual solution to this type of problem is wrap the template function inside a class and pass an object of the class (or simply the type, if the function is wrapped as a static method). That is exactly the solution you have adopted in your working code.
- If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType at every loop iteration?
Question 1 is negative.
- Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?
I don't know C++20 enough to respond this question but I suppose not passing a set of function.
Anyway, you can do a sort of compile-time for loop using std::make_index_sequence/std::index_sequence starting from C++14.
By example, if you accept to extract the touple value outside your myprint() function, you can wrap it inside a lambda and write something as follows (using also C++17 template folding; in C++14 is a little more complicated)
#include
#include
#include
#include
template
void myprint (T const & t)
{ std::cout << t << " "; }
template
void ctf_helper (std::index_sequence, F f, std::tuple const & t)
{ (f(std::get(t)), ...); }
template
void compile_time_for (F f, std::tuple const & t)
{ ctf_helper(std::make_index_sequence{}, f, t); }
int main()
{
std::tuple x{1, 2, "hello"};
compile_time_for<0, 3>([](auto const & v){ myprint(v); }, x);
return 0;
}
If you really want extract the tuple element (or tuples elements) inside the function, the best I can imagine is transform your first example as follows
#include
#include
#include
#include
template class OT,
std::size_t ... Is, typename... Args>
void ctf_helper (std::index_sequence const &, Args && ... args)
{ (OT{}(std::forward(args)...), ...); }
template class OT, typename... Args>
void compile_time_for (Args && ... args)
{ ctf_helper(std::make_index_sequence{},
std::forward(args)...); }
template
struct print_tuple_i
{
template
void operator() (std::tuple const & x)
{ std::cout << std::get(x) << " "; }
};
int main()
{
std::tuple x{1, 2, "hello"};
compile_time_for<0u, 3u, print_tuple_i>(x);
return 0;
}
-- EDIT --
The OP asks
Is there some advantage of using index_sequence over my first code?
I'm not an expert but this way you avoid recursion. Compilers have recursion limits, from the template point of view, that can be strict. This way you avoid they.
Also, your code does not compile if you set the template parameters
end > start. (One can imagine a situation where you want the compiler to determine if a loop is instantiated at all)
I suppose you mean that my code does not compile if start > end.
The bad part is that there aren't check about this problem so the compiler try to compile my code also in this case; so encounter
std::make_index_sequence{}
where end - start is a negative number but used by a template that expect an unsigned number. So end - start become a very great positive number and this can cause problems.
You can avoid this problem imposing a static_assert() inside compile_time_for()
template class OT, typename... Args>
void compile_time_for (Args && ... args)
{
static_assert( end >= start, "start is bigger than end");
ctf_helper(std::make_index_sequence{},
std::forward(args)...);
}
Or maybe you can use SFINAE to disable the function
template class OT, typename... Args>
std::enable_if_t<(start <= end)> compile_time_for (Args && ... args)
{ ctf_helper(std::make_index_sequence{},
std::forward(args)...); }
If you want, using SFINAE you can add an overloaded compile_time_for() version to manage the end < start case
template class OT, typename ... Args>
std::enable_if_t<(start > end)> compile_time_for (Args && ...)
{ /* manage the end < start case in some way */ }