C++
std algorithms
function objects
pass by reference
programming techniques

Passing function objects into std algorithms by 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

Standard C++ algorithms usually take callable objects by value, which means the algorithm receives its own copy. If you need the algorithm to operate on the original function object instead, wrap it with std::ref.

Why Algorithms Normally Copy the Callable

Most standard algorithms are designed to accept predicates, comparators, and functors as regular value parameters. That keeps the interface simple and works well for lightweight stateless callables.

But it also means state changes inside the functor often happen on a copy rather than on the original object.

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5struct Counter {
6    int count = 0;
7
8    void operator()(int) {
9        ++count;
10    }
11};
12
13int main() {
14    std::vector<int> values{1, 2, 3, 4};
15    Counter counter;
16
17    std::for_each(values.begin(), values.end(), counter);
18    std::cout << counter.count << '\n'; // often prints 0
19}

std::for_each increments its internal copy of counter. The original object outside the algorithm does not necessarily change.

Use std::ref to Pass by Reference

If you want the algorithm to invoke the original object, use std::ref.

cpp
1#include <algorithm>
2#include <functional>
3#include <iostream>
4#include <vector>
5
6struct Counter {
7    int count = 0;
8
9    void operator()(int) {
10        ++count;
11    }
12};
13
14int main() {
15    std::vector<int> values{1, 2, 3, 4};
16    Counter counter;
17
18    std::for_each(values.begin(), values.end(), std::ref(counter));
19    std::cout << counter.count << '\n'; // prints 4
20}

std::ref creates a reference_wrapper, which is copyable but still forwards calls to the original object.

When Reference Passing Matters

Passing the functor by reference is useful when:

  • The functor carries state that should be updated
  • Copying the functor would be expensive
  • You want several algorithm invocations to share the same stateful callable

For tiny stateless lambdas, none of this usually matters. The default copy behavior is fine and often preferable.

Lambdas and Captures

Lambdas follow the same idea. The lambda object itself is still passed by value into the algorithm unless you wrap it. However, the lambda may already capture outside variables by reference.

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values{1, 2, 3, 4};
7    int count = 0;
8
9    std::for_each(values.begin(), values.end(), [&](int) {
10        ++count;
11    });
12
13    std::cout << count << '\n'; // prints 4
14}

This works because the copied lambda still holds a reference capture to count. That is often simpler than building a named functor just to share state.

Not Every Algorithm Treats State the Same Way

std::for_each is commonly used in these discussions because it returns the function object by value, which can preserve updated state if you store the return value. Many other algorithms do not do that, so std::ref is the more generally reliable tool when you need shared state across the call. That makes the reference intent explicit. It also improves readability for maintainers. The call site explains the ownership choice.

Common Pitfalls

  • Many algorithms copy the callable, so mutating state inside it may not affect the original object.
  • 'std::ref is the usual tool when the algorithm should act on the original functor.'
  • Returning functors from algorithms such as std::for_each can preserve updated state, but that is algorithm-specific and easy to forget.
  • Reference passing is helpful for stateful callables, but unnecessary for small stateless predicates.

Summary

  • Standard algorithms generally receive callables by value.
  • Use std::ref when you need reference semantics for a function object.
  • Stateful functors are the main reason this matters.
  • Lambdas with reference captures can sometimes solve the same problem more simply.

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.