# std::accumulate

< cpp‎ | algorithm

定义于头文件 `<numeric>` |  |
| (1) |
template< class InputIt, class T > T accumulate( InputIt first, InputIt last, T init ); |  | (C++20 前)
template< class InputIt, class T > constexpr T accumulate( InputIt first, InputIt last, T init ); |  | (C++20 起)
| (2) |
template< class InputIt, class T, class BinaryOperation > T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); |  | (C++20 前)
template< class InputIt, class T, class BinaryOperation > constexpr T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); |  | (C++20 起)

计算给定值 `init` 与给定范围 `[first, last)` 中元素的和。第一版本用 `operator+` ，第二版本用二元函数 `op` 求和元素，均将 `std::move` 应用到其左侧运算数 (C++20 起)。

`op` 必须无副作用。 | (C++11 前)
`op` 必须不非法化涉及范围的任何迭代器，含尾迭代器，且不修改其所涉及范围的任何元素及 `*last` 。 | (C++11 起)

### 参数

first, last | - | 要求和的元素范围
init | - | 和的初值
op | - | 被使用的二元函数对象。接收当前积累值 `a` （初始化为 `init` ）和当前元素 `b` 的二元运算符。 该函数的签名应当等价于： Ret fun(const Type1 &a, const Type2 &b); 签名中并不需要有 const &。 类型 Type1 必须使得 T 类型的对象能隐式转换到 Type1 。类型 Type2 必须使得 InputIt 类型的对象能在解引用后隐式转换到 Type2 。 类型 Ret 必须使得 T 类型对象能被赋 Ret 类型值。 ​
类型要求
- `InputIt` 必须满足老式输入迭代器 (LegacyInputIterator) 的要求。
- `T` 必须满足可复制赋值 (CopyAssignable) 和 可复制构造 (CopyConstructible) 的要求。

### 返回值

1) 给定值与给定范围中的元素的和。
2) 给定范围在 `op` 上左折叠的结果

### 注解

`std::accumulate` 进行左折叠。为进行右折叠，必须逆转二元运算符的参数顺序，并使用逆序迭代器。

#### 最常见的错误

在 `init` 的类型上进行 `op` ，这能引入不期望的元素转换，例如 std::accumulate(v.begin(), v.end(), 0) 在 `v` 为 std::vector<double> 时给出错误的结果。

### 可能的实现

版本一
    template<class InputIt, class T>
    constexpr // C++20 起
    T accumulate(InputIt first, InputIt last, T init)
    {
        for (; first != last; ++first) {
            init = std::move(init) + *first; // C++20 起有 std::move
        }
        return init;
    }
版本二
    template<class InputIt, class T, class BinaryOperation>
    constexpr // C++20 起
    T accumulate(InputIt first, InputIt last, T init,
                 BinaryOperation op)
    {
        for (; first != last; ++first) {
            init = op(std::move(init), *first); // C++20 起有 std::move
        }
        return init;
    }

### 示例

    #include <iostream>
    #include <vector>
    #include <numeric>
    #include <string>
    #include <functional>

    int main()
    {
        std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        int sum = std::accumulate(v.begin(), v.end(), 0);

        int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());

        auto dash_fold = [](std::string a, int b) {
                             return std::move(a) + '-' + std::to_string(b);
                         };

        std::string s = std::accumulate(std::next(v.begin()), v.end(),
                                        std::to_string(v[0]), // 用首元素开始
                                        dash_fold);

        // 使用逆向迭代器右折叠
        std::string rs = std::accumulate(std::next(v.rbegin()), v.rend(),
                                         std::to_string(v.back()), // 用首元素开始
                                         dash_fold);

        std::cout << "sum: " << sum << '\n'
                  << "product: " << product << '\n'
                  << "dash-separated string: " << s << '\n'
                  << "dash-separated string (right-folded): " << rs << '\n';
    }

输出：

    sum: 55
    product: 3628800
    dash-separated string: 1-2-3-4-5-6-7-8-9-10
    dash-separated string (right-folded): 10-9-8-7-6-5-4-3-2-1

### 参阅

adjacent_difference | 计算范围内各相邻元素之间的差 (函数模板)
inner_product | 计算两个范围的元素的内积 (函数模板)
partial_sum | 计算范围内元素的部分和 (函数模板)
reduce (C++17) | 类似 std::accumulate，但不依序执行 (函数模板)