Passing a variadic function as argument

点点圈 提交于 2019-12-13 07:19:54

问题


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?


回答1:


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




回答2:


A template is not a single thing. You cannot pass a template function as a function or an object. Now the name of an overload set (say, foo) can resolve to a single instance of a template function in particular contexts (where you call it, or convert it to a function pointer), which is probably what fooled you.

If you want to work with an entire overload set as an object, you can approximate it via:

struct foo_f{
  template<class...Args>
  auto operator()(Args&&...args)const->
  decltype(foo(std::declval<Args>()...))
  { return foo(std::forward<Args>(args)...); }
};

and now an instance of foo_f approximates the overload set of foo as a single object. Pass foo_f{} in place of foo.

In C++14, alternatively:

[](auto&&...args)->decltype(auto){return foo(decltype(args)(args)...);}

is a lambda that behaves much like foo_f above.



来源:https://stackoverflow.com/questions/31233037/passing-a-variadic-function-as-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!