C++
std::accumulate
C++ STL
algorithm
programming

stdaccumulate with a reference?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

std::accumulate is designed around a running value that is passed and returned by value. That means it does not naturally "accumulate into a reference" the way some developers first expect, although you can still update external state indirectly if you really need to.

Core Sections

How std::accumulate Works

The usual form looks like this:

cpp
1#include <iostream>
2#include <numeric>
3#include <vector>
4
5int main() {
6    std::vector<int> values = {1, 2, 3, 4};
7
8    int sum = std::accumulate(values.begin(), values.end(), 0);
9    std::cout << sum << '\n';
10}

The third argument is the initial accumulator value. Conceptually, std::accumulate keeps producing a new accumulator from the old accumulator and the next element. The binary operation signature is effectively:

cpp
T op(T acc, U value);

That is why the accumulator type T is value-oriented. You return the next accumulated value each time.

The Normal Solution: Return the New Value

If your goal is to sum, concatenate, or combine values, the clean approach is to let std::accumulate produce the result and assign it afterward.

cpp
1#include <numeric>
2#include <string>
3#include <vector>
4
5int main() {
6    std::vector<std::string> parts = {"C++", " ", "STL"};
7
8    std::string result = std::accumulate(
9        parts.begin(),
10        parts.end(),
11        std::string{},
12        [](std::string acc, const std::string& part) {
13            acc += part;
14            return acc;
15        }
16    );
17}

This is the pattern std::accumulate is built for.

Why Raw Reference Accumulators Are Awkward

Trying to make the accumulator itself a raw reference type is usually the wrong direction. The algorithm copies or moves the accumulator value through each step, so reference semantics do not match the intended design well.

If what you really want is "update this existing object," the clearer choices are often:

  • assign the final return value to that object
  • use a loop
  • use std::for_each with a reference capture

For example:

cpp
1#include <algorithm>
2#include <vector>
3
4int main() {
5    std::vector<int> values = {1, 2, 3, 4};
6    int sum = 0;
7
8    std::for_each(values.begin(), values.end(), [&](int value) {
9        sum += value;
10    });
11}

That expresses mutation directly instead of forcing it through a value-oriented algorithm.

Using std::reference_wrapper

If you absolutely need reference-like behavior, std::reference_wrapper can be used as the accumulator type, but it is usually more confusing than helpful.

cpp
1#include <functional>
2#include <iostream>
3#include <numeric>
4#include <vector>
5
6int main() {
7    std::vector<int> values = {1, 2, 3, 4};
8    int total = 0;
9
10    auto result = std::accumulate(
11        values.begin(),
12        values.end(),
13        std::ref(total),
14        [](std::reference_wrapper<int> acc, int value) {
15            acc.get() += value;
16            return acc;
17        }
18    );
19
20    std::cout << total << '\n';
21    std::cout << result.get() << '\n';
22}

This can work, but it is rarely the best answer. It makes a simple accumulation harder to read and gives up much of the clarity that makes standard algorithms attractive.

Choose the Algorithm That Matches the Intent

std::accumulate is excellent when you want to reduce a sequence to one value. It is less compelling when the real goal is side effects on an existing object.

Use it when:

  • you want a computed result
  • the operation is naturally expressed as "old accumulator plus next element"
  • returning a new value on each step is clear

Prefer a loop or another algorithm when:

  • you are mutating external state
  • the operation has significant side effects
  • the accumulator object is large and awkward to copy

In modern C++, clarity matters more than forcing a standard algorithm into a job it was not designed for.

Common Pitfalls

  • Assuming std::accumulate mutates the accumulator by reference and does not need a meaningful return value.
  • Choosing an initial value whose type accidentally forces the wrong accumulator type.
  • Forcing side-effect-heavy mutation into std::accumulate when a loop would express the intent more clearly.
  • Reaching for std::reference_wrapper before checking whether the algorithm choice itself is wrong.
  • Ignoring copy or move cost when the accumulator object is large or awkward to rebuild repeatedly.

Summary

  • 'std::accumulate is fundamentally value-based, not reference-based.'
  • The normal pattern is to return the next accumulated value and assign the final result.
  • If you want to mutate an external variable, a loop or std::for_each is often clearer.
  • 'std::reference_wrapper can simulate reference behavior, but it is rarely the best design.'
  • Choose the algorithm that matches whether your goal is reduction or mutation.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.