如何在c ++ 11中使用可变参数模板生成左关联表达式(又称左折叠)?

drw*_*ode 11 c++ templates fold variadic-templates c++11

我想使用c ++模板使用二进制操作聚合(折叠)多个参数.

这样的模板可以使用如下:

fold<add>(100,10,5) 扩展到 add(add(100, 10), 5)

上面显示的特定扩展是"左侧折叠".扩张add(100, add(10, 5))是"正确的折叠".假设add函数执行简单的整数加法,左右两次折叠产生相同的结果,115.

但考虑div执行整数除法(div(a,b)=a/b)的函数.在这种情况下,关联性很重要,左右折叠会产生不同的结果:

fold_left<div>(100,10,5)  --> div(div(100, 10), 5) --> div(10, 5) -->  2
fold_right<div>(100,10,5) --> div(100, div(10, 5)) --> div(100, 2) --> 50
Run Code Online (Sandbox Code Playgroud)

使用可变参数模板生成右关联版本(fold_right)很简单,但我无法弄清楚如何生成左关联版本(fold_left).以下尝试实现会fold_left导致编译器错误:

#include <iostream>

template <typename T> using binary_op = T(*)(const T& a, const T& b);

// The terminal (single-argument) cases of the variadic functions defined later. 
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }

// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest> 
inline T fold_right(const T& t, Rest... rest) {
    return Operation(t, fold_right<T, Operation>(rest...));
}

// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest> 
inline T fold_left(Rest... rest, const T& t) {
    return Operation(fold_left<T, Operation>(rest...), t);
}

inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }

int main() {
    std::cout << fold_right<int,div>(100,10,5) //  (100 / (10 / 5))  = 50
              << "\n"
              << fold_left<int,div>(100,10,5)  //  Compiler error!
              << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如何使用可变参数模板(在c ++ 11中)来纠正实现fold_left

我认为它本质上归结为能够"弹出"参数包的最后一个参数,我试图在left_fold上面的模板中做,但正如我所说,这导致编译器错误.

注意:我在这个问题中使用了简单的算术运算和整数作为例子,但答案应该是通用的,足以使用任意函数处理对象的聚合(假设它返回与其参数相同类型的对象).

注2:对于那些熟悉c ++ 17的人来说,折叠表达式可用于生成带有二元运算符的左右折叠.但这些在c ++ 11中不可用.

作为一个相关问题:上述模板需要明确指定类型T,如fold_right<int,div>(...).是否有某种方法来制定模板,以便只需要操作,例如fold_right<div>(...).我认为类型T可以推断,但我没有看到一种方法来命令模板参数放置第binary_op<>一个.

谢谢!

Mic*_*ler 10

左侧的参数包有问题.更好地重新实现它作为右​​边的参数包:

template<typename T, binary_op<T> Operation> 
inline T fold_left(const T& t) { return t; }

template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
    return fold_left<T, Operation>(Operation(a,b), rest...);
}
Run Code Online (Sandbox Code Playgroud)