READMEC++

README

Functions / Inline Lambda

Concept Lesson
Beginner
4 min

Learning Objective

Understand Functions well enough to explain it, recognize it in C++, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

Table Of ContentsInline FunctionsWhat Is InlineWhen To Use InlineInline Behavior
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

README
2 min read18 headings

Inline Functions and Lambda Expressions in C++

Table of Contents

  1. Inline Functions
  2. Lambda Expressions
  3. Lambda Captures
  4. Generic Lambdas
  5. Lambdas with STL
  6. std::function
  7. Best Practices

Inline Functions

What is Inline?

The inline keyword suggests the compiler replace function calls with the function body, eliminating call overhead.

inline int square(int x) {
    return x * x;
}

// Compiler may replace:
int y = square(5);
// With:
int y = 5 * 5;

When to Use Inline

  • Small functions (1-3 lines)
  • Frequently called in performance-critical code
  • Getters/setters in classes

Inline Behavior

inline int add(int a, int b) { return a + b; }  // Hint to compiler

// Modern compilers decide themselves - inline is now mostly about linkage
// Defined in header = implicitly inline-able

Class Member Functions

Functions defined inside a class are implicitly inline:

class Point {
public:
    int getX() const { return x; }  // Implicitly inline
    int getY() const;               // Not inline unless defined with 'inline'
private:
    int x, y;
};

inline int Point::getY() const { return y; }  // Explicit inline

Modern C++: constexpr

For compile-time computation, prefer constexpr:

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int arr[factorial(5)];  // Array of 120 elements - computed at compile time!

Lambda Expressions

What are Lambdas?

Anonymous functions defined inline. Introduced in C++11.

Basic Syntax

[captures](parameters) -> return_type { body }

// Simplest lambda
[]() { cout << "Hello!" << endl; }

// With parameters
[](int x, int y) { return x + y; }

// With explicit return type
[](double x) -> int { return static_cast<int>(x); }

Calling Lambdas

// Store in variable
auto greet = []() { cout << "Hello!" << endl; };
greet();  // Prints: Hello!

// Call immediately
[]() { cout << "Immediate!" << endl; }();

// Pass to function
sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

Return Type Deduction

// Usually auto-deduced
auto add = [](int a, int b) { return a + b; };  // Returns int

// Explicit when needed
auto divide = [](int a, int b) -> double {
    return static_cast<double>(a) / b;
};

Lambda Captures

Capture Clause []

Captures variables from the enclosing scope.

Capture Modes

SyntaxMeaning
[]No captures
[x]Capture x by value (copy)
[&x]Capture x by reference
[=]Capture all by value
[&]Capture all by reference
[=, &x]All by value, x by reference
[&, x]All by reference, x by value
[this]Capture this pointer
[*this]Capture this object by value (C++17)

Examples

int x = 10;
int y = 20;

// Capture x by value
auto f1 = [x]() { return x * 2; };  // x is copied

// Capture y by reference
auto f2 = [&y]() { y++; };  // Modifies original y

// Capture all by value
auto f3 = [=]() { return x + y; };

// Capture all by reference
auto f4 = [&]() { x++; y++; };

// Mixed
auto f5 = [=, &y]() {
    y = x * 2;  // x is copy, y is reference
};

Mutable Lambdas

By default, captured-by-value variables are const. Use mutable to modify copies:

int count = 0;
auto counter = [count]() mutable {
    return ++count;  // Modifies the copy
};

cout << counter() << endl;  // 1
cout << counter() << endl;  // 2
cout << count << endl;      // 0 (original unchanged)

Init Captures (C++14)

Create new variables in the capture:

auto ptr = make_unique<int>(42);

// Move into lambda
auto lambda = [p = move(ptr)]() {
    return *p;
};

// Create new variable
auto lambda2 = [value = x + y]() {
    return value * 2;
};

Generic Lambdas

Auto Parameters (C++14)

auto add = [](auto a, auto b) { return a + b; };

cout << add(1, 2) << endl;       // int
cout << add(1.5, 2.5) << endl;   // double
cout << add(string("a"), string("b")) << endl;  // string

Template Lambdas (C++20)

auto print = []<typename T>(const vector<T>& v) {
    for (const auto& elem : v) {
        cout << elem << " ";
    }
    cout << endl;
};

Lambdas with STL

Sorting

vector<int> nums = {3, 1, 4, 1, 5, 9};

// Ascending (default)
sort(nums.begin(), nums.end());

// Descending
sort(nums.begin(), nums.end(), [](int a, int b) {
    return a > b;
});

// Custom objects
vector<Person> people = {...};
sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
    return a.age < b.age;
});

Finding

vector<int> nums = {1, 2, 3, 4, 5, 6};

// Find first even
auto it = find_if(nums.begin(), nums.end(), [](int n) {
    return n % 2 == 0;
});

// Count greater than 3
int count = count_if(nums.begin(), nums.end(), [](int n) {
    return n > 3;
});

Transforming

vector<int> nums = {1, 2, 3, 4, 5};
vector<int> squares;

transform(nums.begin(), nums.end(), back_inserter(squares), [](int n) {
    return n * n;
});
// squares = {1, 4, 9, 16, 25}

Filtering

vector<int> nums = {1, 2, 3, 4, 5, 6};

// Remove odd numbers (erase-remove idiom)
nums.erase(
    remove_if(nums.begin(), nums.end(), [](int n) {
        return n % 2 != 0;
    }),
    nums.end()
);
// nums = {2, 4, 6}

For Each

vector<int> nums = {1, 2, 3, 4, 5};

for_each(nums.begin(), nums.end(), [](int& n) {
    n *= 2;
});
// nums = {2, 4, 6, 8, 10}

std::function

Type-Erased Function Wrapper

std::function can hold any callable: function, lambda, functor, member function pointer.

#include <functional>

// Declare function type
function<int(int, int)> operation;

// Assign lambda
operation = [](int a, int b) { return a + b; };
cout << operation(2, 3) << endl;  // 5

// Assign different lambda
operation = [](int a, int b) { return a * b; };
cout << operation(2, 3) << endl;  // 6

Storing Lambdas

// Store callbacks
vector<function<void()>> callbacks;

callbacks.push_back([]() { cout << "First!" << endl; });
callbacks.push_back([]() { cout << "Second!" << endl; });

for (auto& cb : callbacks) {
    cb();
}

Function as Parameter

void applyOperation(vector<int>& v, function<int(int)> op) {
    for (int& n : v) {
        n = op(n);
    }
}

vector<int> nums = {1, 2, 3};
applyOperation(nums, [](int n) { return n * 2; });
// nums = {2, 4, 6}

Performance Note

std::function has overhead due to type erasure. For templates, prefer:

// More efficient - no type erasure
template<typename F>
void apply(vector<int>& v, F func) {
    for (int& n : v) {
        n = func(n);
    }
}

Best Practices

✅ Do

// Use lambdas for short, one-off operations
sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// Capture by reference for large objects
auto process = [&bigData]() { /* use bigData */ };

// Use auto for lambda type
auto lambda = [](int x) { return x * 2; };

// Use init captures for moves
auto lambda = [ptr = move(uniquePtr)]() { /* use ptr */ };

❌ Don't

// Avoid capturing by reference if lambda outlives the variable
auto createLambda() {
    int x = 10;
    return [&x]() { return x; };  // DANGER: x destroyed!
}

// Avoid complex lambdas - use named functions instead
auto complicated = [](/* many params */) {
    // 50 lines of code...
};  // Bad - extract to named function

// Avoid unnecessary std::function overhead
std::function<int(int)> f = [](int x) { return x; };  // Overhead
auto f = [](int x) { return x; };  // Better

Guidelines

  1. Keep lambdas short - if > 5 lines, consider a named function
  2. Capture explicitly - avoid [=] and [&] when possible
  3. Watch lifetimes - captured references must outlive lambda
  4. Use auto - each lambda has a unique type
  5. Prefer templates over std::function when possible

Quick Reference

// Inline function
inline int square(int x) { return x * x; }

// Lambda syntax
[captures](params) -> return_type { body }

// Common patterns
[]() { }                    // No captures, no params
[](int x) { return x*2; }   // Parameter, auto return
[x]() { return x; }         // Capture by value
[&x]() { x++; }             // Capture by reference
[=]() { }                   // All by value
[&]() { }                   // All by reference
[x]() mutable { x++; }      // Modify copy

// Store in variable
auto f = [](int x) { return x * 2; };

// Pass to algorithm
sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// std::function
function<int(int)> f = [](int x) { return x * 2; };

Compile & Run

g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue