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.
Gdzie jest wyrażenie _ambura_? – nosid
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