问题
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