【问题标题】:How can i combine multiple variadic templates or split parameter pack?如何组合多个可变参数模板或拆分参数包?
【发布时间】:2022-01-05 04:03:27
【问题描述】:

我目前正在尝试为 std::functions 定义一个通用乘法运算符。我想使用多个可变参数模板来做到这一点。部分特化如下所示:

template <typename... _Type> using op_Type = typename std::function<u64(u64, _Type...)>;

inline auto operator*(const op_Type<>& f, const op_Type<>& g) -> op_Type<> {
    return [f, g](u64 num) {
        return f(g(num));
    };
}
template <typename _Ty1, typename _Ty2>
inline auto operator*(const op_Type<_Ty1>& f, const typename op_Type<_Ty2>& g)
-> op_Type<_Ty1, _Ty2> {
    return [f, g](u64 num, _Ty1 arg1, _Ty2 arg2) {
        return f(g(num, arg2), arg1);
    };
}
template <typename _Tyf1, typename _Tyf2, typename _Tyg1, typename _Tyg2>
inline auto operator*(const op_Type<_Tyf1, _Tyf2>& f,
    const typename op_Type<_Tyg1, _Tyg2>& g)
    -> op_Type<_Tyf1, _Tyf2, _Tyg1, _Tyg2> {
    return [f, g](u64 num, _Tyf1 argF1, _Tyf2 argF2, _Tyg1 argG1, _Tyg2 argG2) {
        return f(g(num, argG1, argG2), argF1, argF2);
    };
}

但我需要的是任何通用 std::functions 采用 u64 值和任意数量的其他参数的相同行为,应该如下所示:

template <typename... _Ty1, template <typename...> class op1 
        , typename... _Ty2, template <typename...> class op2
        , typename... _RetTy, template<typename...> class opRet>
inline auto operator*(const op1<_Ty1...>& f, const op2<_Ty2...> g) -> opRet<_RetTy...> {
    const int size1 = sizeof...(_Ty1);
    const int size2 = sizeof...(_Ty2);
    return [f, g](u64 num, _Ty1 args1..., _Ty2 args2...) {
        auto tmp = g(num, std::forward<_Ty1>(args1)...);
        return f(tmp, std::forward<_Ty2>(args2)...);
    };
}

我也想删除添加的类模板,但可能无法使用多个可变参数模板,因为编译器不知道可变参数模板何时结束,对吧? 我想一个好的解决方法是拆分参数包:

template <typename... _Ty1, , typename... _Ty2>
inline auto operator*(const op_type<_Ty1...>& f, const op_type<_Ty2...> g) -> opRet<_Ty1...,_Ty2...> {
    const int size1 = sizeof...(_Ty1);
    const int size2 = sizeof...(_Ty2);
    return [f, g](u64 num, variadic template args...) {
        auto tmp = g(num, split_args(args, 0, size1 - 1));
        return f(tmp, split_args(args, remaining_arguments);
    };
}

其中 split_args 返回输入索引之间的参数,但我不确定如何实现它,有什么想法吗? 我找到了这样的解决方案: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0535r0.html 但我不确定是否有可用的开源代码

编辑: 总之,我需要一个看起来像这样的函数:

template <typename... _Ty1, typename... _Ty2>
   inline auto operator*(const op_type<_Ty1...>& f, const op_type<_Ty2...> g) -> op_type<_Ty1...,_Ty2...> {
        return [f, g](u64 num, _Ty1 args1..., _Ty2 args2...) {
            auto tmp = g(num, std::forward<_Ty1>(args1)...);
            return f(tmp, std::forward<_Ty2>(args2)...);
        };
    }

编辑2: 用法: 假设我有两个功能:

auto f(u64 num, int i, int j) -> u64{
     return num + (i - j)^i;
}

auto g(u64 num, double x){
     return num - int(num / x);
}

那么乘法运算符 h = f*g 应该将 h 返回为:

auto h(u64 num, int i, int j, double x) -> u64{
     num + (i - j)^i - int(num / x);
}

【问题讨论】:

  • 你能解释一下你真正想要做什么吗?我看到很多代码,但我没有明白它应该做什么!
  • 我想重载采用两个 std::functions 的乘法运算符,其中每个函数都可以采用任意数量的参数(因此是可变参数模板)。所以一般来说我需要一个在两个不同的可变参数模板上定义的函数,该函数的输入参数是两个带有不同可变参数模板的 std::functions(这里通过 op_type 定义)并返回一个结合这些可变参数模板的函数
  • Q1:两个 std::function 都应该用相同的函数“加载”吗? Q2:组合意味着,你想返回一个 std::function ,它与一个给定的函数对象相同,可能是你的操作符的第一个参数,并返回这个带有两个参数列表的函数对象?这意味着,您的功能对象必须能够自己生成功能对象,对吗?你能在你的例子中展示函数对象和用法吗?
  • 如果运算符对象的第一个参数始终是 uint64_t,为什么其他参数不同?
  • A1:不,关键是功能可以不同; A2:通过组合我的意思是,如果一个 std::function 采用 (u64,int,int) 而另一个采用 (u64, double) 乘法产生的 std::function 应该采用 (u64, int, int, double ),所以是的,这两个参数都是 u64 常见的,所以我不需要重复它;作为使用示例,我将编辑问题

标签: c++ variadic-templates template-meta-programming parameter-pack


【解决方案1】:

希望我能得到你的愿望......


template< typename PACK1, typename PACK2 > struct Combined;

template < typename ... PACK1, typename ... PACK2 >
struct Combined< std::function< uint64_t( uint64_t, PACK1... )>, std::function< uint64_t(uint64_t, PACK2...) > >
{
    using OP_TYPE1 = std::function< uint64_t( uint64_t, PACK1... )>;
    using OP_TYPE2 = std::function< uint64_t( uint64_t, PACK2... )>;

    OP_TYPE1 op1;
    OP_TYPE2 op2;
    Combined( OP_TYPE1 op1_, OP_TYPE2 op2_ ): op1{ op1_}, op2{ op2_}{}

    auto operator( )(uint64_t p1, PACK1... args1, PACK2... args2)
    {
        return op2( op1( p1, args1...), args2...);
    }
};


template < typename OP_TYPE1, typename OP_TYPE2> auto operator*(OP_TYPE1, OP_TYPE2);

template < typename ... PACK1, typename ... PACK2 >
auto operator* ( std::function< uint64_t( uint64_t, PACK1... )> op1, std::function< uint64_t(uint64_t, PACK2...) > op2 )
{
    return Combined< std::function< uint64_t( uint64_t, PACK1... )>, std::function< uint64_t(uint64_t, PACK2...)>>{ op1, op2 };
}

// Example funcs
auto f(uint64_t num, int i, int j) -> uint64_t{
     return num + ((i - j)^i);
}

uint64_t g(uint64_t num, double x){
     return num - int(num / x);
}


int main()
{
    std::function fobject = f;
    std::function gobject = g;

    auto fg = fobject*gobject;

    std::cout << fg( 1, 2, 3, 6.66 ) << std::endl;
}

该示例遗漏了所有可以通过转发参数、移动等进行优化的内容。它只是让你捕捉签名并从模板参数中获取参数等等。

【讨论】:

  • 这真的可以编译和工作吗?我知道我在函数调用中遇到了两个不同参数包的问题,​​就像您在这一行中遇到的那样:auto operator()(uint64_t p1, PACK1...args1, PACK2...args2)。我想这是 C++20 的一个特性,因为折叠表达式 : op2( op1( p1, args1...), args2...) 在我使用 C++17
  • 哦!我知道了。由于没有参数包的前向声明,编译器知道每个可变参数模板应该在哪里结束。不知道。这很好用。非常感谢!!
猜你喜欢
  • 2011-07-25
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2015-07-07
  • 1970-01-01
相关资源
最近更新 更多