2014-05-12 9 views
11

Chciałbym zadeklarować funkcję lambda z dokładnie N parametrami, gdzie N jest argumentem szablonu. Coś jak ...Funkcja Lambda z wieloma argumentami określonymi podczas kompilacji

template <int N> 
class A { 
    std::function<void (double, ..., double)> func; 
         // exactly n inputs 
}; 

Nie mogłem wymyślić, jak to zrobić z paradygmatem metafunkcji.

+3

Gdzie jest wyrażenie _ambura_? – nosid

+0

Klasa będzie przechowywać wyrażenie lambda. Powiedzmy, że jest on zainicjowany w konstruktorze na określenie (nie jest to konieczne w mojej konkretnej aplikacji). – parsiad

Odpowiedz

14

Możesz napisać szablon n_ary_function z zagnieżdżonym typedef type. Ten typ może być stosowany w sposób następujący:

template <int N> 
class A { 
    typename n_ary_function<N, double>::type func; 
}; 

Zgodnie z definicją n_ary_function:

template <std::size_t N, typename Type, typename ...Types> 
struct n_ary_function 
{ 
    using type = typename n_ary_function<N - 1, Type, Type, Types...>::type; 
}; 

template <typename Type, typename ...Types> 
struct n_ary_function<0, Type, Types...> 
{ 
    using type = std::function<void(Types...)>; 
}; 
1

Nie można to zrobić bezpośrednio.

You może zrobić coś takiego

template <unsigned N> class UniformTuple; 

template <> 
class UniformTuple <0> 
{ 
}; 

template <unsigned N> 
class UniformTuple : public UniformTuple <N-1> 
{ 
public: 

    template <typename... Args> 
    UniformTuple (double arg, Args... args) 
    : UniformTuple <N-1> (args...) 
    , m_value (arg) 
    { 
    } 

private: 

    double m_value; 
}; 

template <int N> 
class A 
{ 
    std :: function <void (const UniformTuple <N> &)> func; 
}; 
+0

+1. Myślałem o tym podejściu. To nie jest idealne, ale może to być jedyny sposób. – parsiad

+0

Innym pomysłem jest zdefiniowanie własnej klasy Function za pomocą '' 'wirtualnego get (const double * arguments)' 'i implementacji' '' operator() '' 'z szablonem variadic (i wykonania' '' static_assert '' 'w' '' operator() '' '). To też nie jest idealne, ale trochę bliżej tego, co muszę umieć zrobić. – parsiad

3

Meta template że trwa szablon, liczba i rodzaj, a powołuje się na szablon z N egzemplarzy typu:

template<template<class...>class target, unsigned N, class T, class... Ts> 
struct repeat_type_N: repeat_type_N<target, N-1, T, T, Ts...> {}; 
template<template<class...>class target, class T, class... Ts> 
struct repeat_type_N<target, 0, T, Ts...> { 
    typedef target<Ts...> type; 
}; 
template<template<class...>class target, unsigned N, class T> 
using repeat_type_N_times = typename repeat_type_N<target, N, T>::type; 

Teraz używamy go:

template<typename... Ts> using operation=void(Ts...); 
template<unsigned N, class T> using N_ary_op = repeat_type_N_times< operation, N, T >; 
template<unsigned N> using N_double_func = N_ary_op<N,double>; 

I testujemy:

void three_doubles(double, double, double) {} 

int main() { 
    N_double_func<3>* ptr = three_doubles; 
    std::function< N_double_func<3> > f = three_doubles; 
} 

i wygraj.

To, czego dokładnie używasz double, double, double, zależy od Ciebie w powyższym systemie. Możesz mieć lambda, na przykład, zainicjować std::function.

Możesz spakować double, double, double do template<class...>struct type_list{};, dzięki czemu możesz przekazać go jako jeden argument do innego template, a następnie specjalizować się, aby go rozpakować.

A repeat_type który ma mniej rekursji dla dużej N:

// package for types. The typedef saves characters later, and is a common pattern in my packages: 
template<class...>struct types{typedef types type;}; 

// Takes a target and a `types`, and applies it. Note that the base has no implementation 
// which leads to errors if you pass a non-`types<>` as the second argument: 
template<template<class...>class target, class types> struct apply_types; 
template<template<class...>class target, class... Ts> 
struct apply_types<target, types<Ts...>>{ 
    typedef target<Ts...> type; 
}; 
// alias boilerplate: 
template<template<class...>class target, class types> 
using apply_types_t=typename apply_types<target,types>::type; 

// divide and conquer, recursively: 
template<unsigned N, class T, class Types=types<>> struct make_types:make_types< 
    (N+1)/2, T, typename make_types<N/2, T, Types>::type 
> {}; 

// terminate recursion at 0 and 1: 
template<class T, class... Types> struct make_types<1, T, types<Types...>>:types<T,Types...> {}; 
template<class T, class Types> struct make_types<0, T, Types>:Types{}; 

// alias boilerplate: 
template<unsigned N, class T> 
using make_types_t=typename make_types<N,T>::type; 

// all of the above reduces `repeat_type_N_t` to a one-liner:  
template<template<class...>class target, unsigned N, class T> 
using repeat_type_N_times = apply_types_t<target, make_types_t<N,T>>; 

przypadku dużych N, powyższe może znacząco zmniejszyć kompilacji razy i radzić sobie z przepełnienia template stos.

+0

@nosid ukradł twoje rozwiązanie, sprawił, że stało się to ogólne i takie tam. – Yakk

Powiązane problemy