2015-07-05 2 views
0

Рассмотрим рабочий код:Передача VARIADIC функции в качестве аргумента

#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 
} 

Я хочу прямо сейчас обобщают Foo-структуру следующим образом:

#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}); 
} 

Но я получаю ошибку компиляции (от GCC 4.9. 2), что F не может быть выведено. Как мне это достичь?

+0

foo - неразрешенная группа методов. Перед тем, как выполнить его, вам нужно выбрать правильный вариант с аргументами для броска или шаблона. – Dani

ответ

2

foo - это семейство перегрузок, поэтому foo неоднозначно.
(даже foo<int, int> есть, как это может быть дополнительный тип тоже).

Вы можете заставить ожидаемый тип функции следующим образом:

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

Альтернативой обернуть функцию foo в класс:

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'; 
    } 

}; 

и сохранить реализацию:

Live Demo

+0

@ Jarod42 Спасибо! А затем обобщение 'fooArray' на использование' f' вместо 'foo'? Это не совсем то же самое, не так ли? Ну, я попробовал, и он работает со вторым методом, но с использованием вашего первого метода? – prestokeys

+0

@prestokeys: 'fooArray' должен иметь второй аргумент, функцию с параметрами' N'. Для создания правильного типа требуется некоторый помощник. – Jarod42

1

Шаблон - это не одна вещь. Вы не можете передавать функцию шаблона как функцию или объект. Теперь имя набора перегрузки (скажем, foo) может быть разрешено для одного экземпляра функции шаблона в определенных контекстах (где вы вызываете его или преобразуете в указатель функции), что, вероятно, вас обмануло.

Если вы хотите работать с целой перегрузки, установленной в качестве объекта, вы можете приблизить его с помощью:

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

и теперь экземпляр foo_f приближает набор перегрузки foo как единый объект. Перевал foo_f{} вместо foo.

В C++ 14, в качестве альтернативы:

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

является лямбда, который ведет себя так же, как foo_f выше.