Search code examples
c++templatesc++14variadic

Passing a variadic function as argument


Consider this working code:

#include <iostream>
#include <utility>
#include <array>

template <typename... Args>
void foo (Args&&... args) {
    const auto v = {args...};
    for (auto x : v) std::cout << x << ' ';     std::cout << '\n';
}

template <typename> struct Foo;

template <std::size_t... Is>
struct Foo<std::index_sequence<Is...>> {
    template <typename Container>
    static void execute (const Container& v) {
        foo(v[Is]...);
    }
};

template <std::size_t N>
void fooArray (const std::array<int, N>& a) {
    Foo<std::make_index_sequence<N>>::execute(a);
}

int main() {
    fooArray<6>({0,1,2,3,4,5});  // 0 1 2 3 4 5
}

I want to now generalize the Foo struct like so:

#include <iostream>
#include <utility>
#include <array>

template <typename... Args>
void foo (Args&&... args) {
    const auto v = {args...};
    for (auto x : v) std::cout << x << ' ';     std::cout << '\n';
}

template <typename> struct Foo;

template <std::size_t... Is>
struct Foo<std::index_sequence<Is...>> {
    template <typename Container, typename F>  // *** Modified
    static void execute (const Container& v, F f) {
        f(v[Is]...);
    }
};

template <std::size_t N>
void fooArray (const std::array<int, N>& a) {
    Foo<std::make_index_sequence<N>>::execute(a, foo);
}

int main() {
    fooArray<6>({0,1,2,3,4,5});
}

But I get a compile error (from GCC 4.9.2) that F cannot be deduced. How do I achieve this?


Solution

  • foo is a family of overloads, and so the foo is ambiguous.
    (even foo<int, int> is, as it may have additional type too).

    You may force expected type function as follow:

    template <std::size_t... Is>
    struct Foo<std::index_sequence<Is...>> {
        template <typename Container>
        static void execute (const Container& v, void (*f)(decltype(v[Is])&...)) {
            f(v[Is]...);
        }
    };
    

    Live example

    An alternative is to wrap function foo into a class:

    class FooCaller
    {
    public:
        template <typename... Args>
        void operator () (Args&&... args) const {
            const auto v = {args...};
            for (auto x : v) std::cout << x << ' ';     std::cout << '\n';
        }
    
    };
    

    and keep your implementation:

    Live Demo