1

As the title says, I'm trying to create something to be used as followed :

template <typename T>
void testFunc(int& i)
{
    ...
}
int i { 0 };
ForEach<int, float>::run<testFunc>(i);

I've already tried some things, but I'm hitting some problems:

template<typename CurrentComponentType, typename... ComponentTypes>
struct ForEach
{
    template<void (&func)(auto&&... args)>
    static constexpr void run(auto&&... args)
    {
        func<CurrentComponentType>(std::forward<decltype(args)>(args)...);
        ForEach<ComponentTypes...>::run<func>(std::forward<decltype(args)>(args)...);
    }
};

template<typename CurrentComponentType>
struct ForEach<CurrentComponentType>
{
    template<void (&func)(auto&&... args)>
    static constexpr void run(auto&&... args)
    {
        func<CurrentComponentType>(std::forward<decltype(args)>(args)...);
    }
};
  1. I don't know how to take a template function as a (template but not necessarily) argument.

  2. For some reasons that I don't understand, I cannot call again the run() function as so: run<func>(. It says '<unresolved overloaded function type>'.

I think there are multiple things I don't understand.

How can I fix it, and why doesn't it work this way? What am I misunderstanding?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
DiantArts
  • 104
  • 7

1 Answers1

0

Each of the solutions below can be used like so:

int main(void)
{
    int i { 0 };
    ForEach<int, float>::run<testFunc>(i);
}

Because you can't pass an uninstantiated template function as a (template) parameter, you need to make it a template class with an operator():

#include <utility>

template<typename T>
struct testFunc
{
    void operator()(int& i) {}
};

template<typename T, typename... Ts>
struct ForEach
{
    template<template<typename> typename F, typename... Args>
    static constexpr void run(Args&&... args)
    {
        ForEach<T>::template run<F>(std::forward<Args>(args)...);
        ForEach<Ts...>::template run<F>(std::forward<Args>(args)...);
    }
};

template<typename T>
struct ForEach<T>
{
    template<template<typename> typename F, typename... Args>
    static constexpr void run(Args&&... args)
    {
        F<T>{}(std::forward<Args>(args)...);
    }
};

Starting in C++17, you can use fold expressions to avoid recursion:

#include <utility>

template<typename T>
struct testFunc
{
    void operator()(int& i) {}
};

template<typename... Ts>
struct ForEach
{
    template<template<typename> typename F, typename... Args>
    static constexpr void run(Args&&... args)
    {
        (F<Ts>{}(std::forward<Args>(args)...), ...);
    }
};

Finally, in C++20 you also have the option to use a lambda with a template parameter list instead:

#include <utility>

auto testFunc = []<typename T>(int& i) {};

template<typename... Ts>
struct ForEach
{
    template<auto F, typename... Args>
    static constexpr void run(Args&&... args)
    {
        (F.template operator()<Ts>(std::forward<Args>(args)...), ...);
    }
};
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153