C++
lambda expressions
std::find_if
C++ programming
functional programming

C lambda expression in stdfind_if?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

std::find_if is one of the most natural places to use a C++ lambda because it expects a predicate and lambdas let you define that predicate inline. The result is usually shorter and clearer than a separately declared functor or helper function. The important parts to understand are the predicate shape, capture rules, and what the returned iterator means.

Basic std::find_if With a Lambda

std::find_if scans a range until the predicate returns true.

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values{3, 7, 10, 14, 19};
7
8    auto it = std::find_if(values.begin(), values.end(), [](int x) {
9        return x % 2 == 0;
10    });
11
12    if (it != values.end()) {
13        std::cout << *it << "\n";
14    }
15}

The lambda takes one element and returns a boolean condition.

Capturing External Values

Lambdas become more powerful when the predicate depends on surrounding state.

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values{5, 8, 12, 21, 34};
7    int threshold = 20;
8
9    auto it = std::find_if(values.begin(), values.end(), [threshold](int x) {
10        return x > threshold;
11    });
12
13    if (it != values.end()) {
14        std::cout << *it << "\n";
15    }
16}

Here threshold is captured by value. That is usually the safest default for read-only conditions.

Capture by Reference

Capture by reference is useful when the predicate should see current state from outside the lambda.

cpp
1int limit = 10;
2auto it = std::find_if(values.begin(), values.end(), [&limit](int x) {
3    return x > limit;
4});

This works, but it also means the lambda depends on external mutable state. Use it intentionally, because reference capture can make reasoning about lifetime and side effects harder.

Working With Structs

Lambdas are especially readable when searching object collections.

cpp
1#include <algorithm>
2#include <iostream>
3#include <string>
4#include <vector>
5
6struct User {
7    int id;
8    std::string email;
9    bool active;
10};
11
12int main() {
13    std::vector<User> users{
14        {1, "[email protected]", false},
15        {2, "[email protected]", true},
16        {3, "[email protected]", true}
17    };
18
19    auto it = std::find_if(users.begin(), users.end(), [](const User& user) {
20        return user.active;
21    });
22
23    if (it != users.end()) {
24        std::cout << it->email << "\n";
25    }
26}

For single-use logic like this, a lambda is usually better than defining a named predicate elsewhere.

Predicate Semantics

The predicate passed to std::find_if should be:

  • cheap to evaluate
  • side-effect free in normal use
  • easy to read

Although you technically can mutate captured state inside a lambda, doing so in a search predicate usually hurts clarity.

Iterator Result Handling

std::find_if returns an iterator, not the element directly. Always compare against the end iterator before dereferencing.

cpp
1auto it = std::find_if(values.begin(), values.end(), [](int x) { return x == 99; });
2if (it == values.end()) {
3    std::cout << "not found\n";
4}

Dereferencing without the end check is undefined behavior if no match exists.

Prefer Algorithms Over Manual Loops When Clear

Equivalent manual loop:

cpp
1for (auto it = values.begin(); it != values.end(); ++it) {
2    if (*it % 2 == 0) {
3        std::cout << *it << "\n";
4        break;
5    }
6}

This is fine, but std::find_if communicates intent more directly: find the first element that matches a predicate.

When a Named Predicate Is Better

If the condition is long or reused, a named function or function object may be clearer than an inline lambda. Lambdas are best when the condition is local to the algorithm and small enough to read in place.

Keep the predicate close to the algorithm, but not at the cost of readability.

Common Pitfalls

  • Forgetting to compare the returned iterator with end() before dereferencing.
  • Capturing by reference when value capture would be safer and clearer.
  • Writing long, complex lambda bodies that should be extracted.
  • Adding side effects to a predicate used only for searching.
  • Confusing std::find_if with std::find, which does direct equality search.

Summary

  • 'std::find_if and lambdas are a natural fit in modern C++.'
  • Use a lambda when the predicate is short and local to the search.
  • Capture outside values intentionally and prefer value capture for read-only logic.
  • Always check the returned iterator before using it.
  • Favor clear predicate design over clever inline complexity.

Course illustration
Course illustration

All Rights Reserved.