【问题标题】:Lambda function with number of arguments determined at compile-time具有在编译时确定的参数数量的 Lambda 函数
【发布时间】:2014-06-30 05:13:27
【问题描述】:

我想声明一个带有 N 个参数的 lambda 函数,其中 N 是一个模板参数。比如……

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

我想不出用元函数范式做到这一点的方法。

【问题讨论】:

  • lambda 表达式在哪里?
  • 该类将存储一个 lambda 表达式。假设它是在构造函数中初始化的(在我的特定应用程序中不一定是这样)。

标签: c++ templates c++11 lambda c++14


【解决方案1】:

您可以使用嵌套的 typedef type 编写模板 n_ary_function。这种类型可以按如下方式使用:

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

以下代码片段包含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...)>;
};

【讨论】:

    【解决方案2】:

    一个元template,它接受一个模板、一个计数和一个类型,并使用该类型的N 副本调用模板:

    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;
    

    现在,我们使用它:

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

    我们对其进行测试:

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

    然后赢了。

    在上述系统中,您使用double, double, double 的具体用途完全取决于您。例如,您可以使用 lambda 来初始化 std::function

    您可以将double, double, double 打包成template&lt;class...&gt;struct type_list{};,这样您就可以将它作为一个参数传递给另一个template,然后专门对其进行解包。

    一个repeat_type 对大型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>>;
    

    对于较大的N,上述方法可以显着减少编译时间,并处理template 堆栈溢出问题。

    【讨论】:

    • @nosid 偷了你的解决方案,把它变成通用的东西。
    【解决方案3】:

    你不能直接这样做。

    可以做这样的事情

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

    【讨论】:

    • +1。我想过这种方法。这并不理想,但它可能是唯一的方法。
    • 另一个想法是使用virtual get(const double *arguments) 定义我自己的Function 类,并使用可变参数模板实现operator()(并在operator() 中执行static_assert)。这也不理想,但更接近我需要做的事情。
    【解决方案4】:

    为了完整起见,这里是一个没有递归的解决方案:

    template <class Ret, class Arg, class Idx>
    struct n_ary_function_;
    
    template <class Ret, class Arg, std::size_t... Idx>
    struct n_ary_function_<Ret, Arg, std::index_sequence<Idx...>> {
        template <class T, std::size_t>
        using id = T;
    
        using type = std::function<Ret(id<Arg, Idx>...)>;
    };
    
    template <class Ret, class Arg, std::size_t N>
    using n_ary_function = typename n_ary_function_<
        Ret, Arg, std::make_index_sequence<N>
    >::type;
    

    See it live on Coliru

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      • 1970-01-01
      • 2022-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多