2014-05-12 3 views
11

Vorrei dichiarare una funzione lambda con esattamente N parametri, dove N è un argomento modello. Qualcosa di simile ...Funzione lambda con numero di argomenti determinati in fase di compilazione

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

non ho potuto pensare a un modo per fare questo con il paradigma metafunction.

+3

Dov'è l'espressione _lambda_? – nosid

+0

La classe memorizzerà un'espressione lambda. Diciamo che è inizializzato nel costruttore per definizione (questo non è necessariamente vero nella mia particolare applicazione). – parsiad

risposta

14

È possibile scrivere un modello n_ary_function con un typedf nidificato type. Questo tipo può essere utilizzato come segue:

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

In seguito alla definizione di 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

Non puoi farlo direttamente.

È può fare qualcosa di simile

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. Ho pensato a questo approccio. Non è l'ideale, ma potrebbe essere l'unico modo. – parsiad

+0

Un'altra idea è definire la mia propria classe Function con un '' 'get virtuale (const double * argomenti)' '' e implementare '' 'operator()' '' con un modello variadic (ed eseguire un '' 'static_assert '' 'in' '' operator() '' '). Anche questo non è l'ideale, ma un po 'più vicino a quello che devo essere in grado di fare. – parsiad

3

Una meta template che prende un modello, un conte, e un tipo, e richiama il modello con N copie del tipo:

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; 

Ora, lo usiamo:

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

E lo testiamo:

void three_doubles(double, double, double) {} 

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

e vincere.

Che cosa si utilizza esattamente il double, double, double per è completamente a voi nel sistema di cui sopra. Puoi avere un lambda che inizializzi un std::function con, per esempio.

È possibile impacchettare double, double, double in un template<class...>struct type_list{}; in modo da poterlo passare come argomento a un altro template, quindi specializzarsi per decomprimerlo.

Un repeat_type che ha meno ricorsione per grandi 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>>; 

Per grandi N, quanto sopra può ridurre significativamente i tempi di compilazione, e trattare con traboccante la pila template.

+0

@nosid ha rubato la tua soluzione, lo rende tutto generico e roba del genere. – Yakk

Problemi correlati