Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Passing Parameters to Lambda Expressions in C++

Tech May 10 3

Lambda expressions, introduced in C++11, provide a powerful mechanism for creating anonymous function objects. While parameter passing to lambdas resembles regular functions, the capture mechanism introduces additional flexibility.

Syntax Structure

[capture](parameters) -> return_type { 
    // function body 
}

Parameter Passing Modes

Pass by Value

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

inttotal = sum(3, 4); // total = 7

Pass by Reference

auto doubleValue = [](int& num) {
    num *= 2;
};

int n = 5;
doubleValue(n); // n becomes 10

Const Reference Passing

auto displayArray = [](const std::vector<int>& data) {
    for (const auto& element : data) {
        std::cout << element << " ";
    }
    std::cout << std::endl;
};

std::vector<int> data = {1, 2, 3, 4, 5};
displayArray(data); // avoids copying the entire vector

Capture List Mechanisms

Lambdas can access variables from the enclosing scope through the capture list—a capability absent from regular functions:

Capture by Value

int count = 10;
auto show = [count]() {
    std::cout << count << std::endl;
    // count++; // Error: captured-by-value variables are const by default
};
show(); // prints 10

Capture by Reference

int count = 10;
auto increment = [&count]() {
    std::cout << count << std::endl;
    count++; // modifies the original variable
};
increment(); // prints 10
std::cout << count << std::endl; // prints 11

Mutable Keyword

int count = 10;
auto modifyLocal = [count]() mutable {
    std::cout << count << std::endl;
    count++; // modifies the local copy
    std::cout << count << std::endl;
};
modifyLocal(); // prints 10, then 11
std::cout << count << std::endl; // prints 10 (original unchanged)

Generalized References and Perfect Forwarding

C++14 introduced initializing captures for more flexible parameter handling:

// Generic lambda in C++14
auto forwarder = [](auto&& arg) {
    return std::forward<decltype(arg)>(arg);
};

int m = 5;
const int n = 10;

auto r1 = forwarder(m); // lvalue passed
auto r2 = forwarder(42); // rvalue passed
auto r3 = forwarder(n); // const lvalue passed

Capturing the this Pointer

When used as class member functions, lambdas can capture this to access member variables:

class Processor {
public:
    int data = 42;
    
    void showData() {
        auto printer = [this]() {
            std::cout << data << std::endl;
        };
        printer();
    }
};

Default Capture Modes

int a = 10, b = 20;

// Capture all external variables by value
auto fn1 = [=]() {
    std::cout << a + b << std::endl;
};

// Capture all external variables by reference
auto fn2 = [&]() {
    a++; b++;
    std::cout << a + b << std::endl;
};

// Mixed capture: a by value, b by reference
auto fn3 = [=, &b]() {
    // a is captured by value, cannot be modified
    b++; // b is captured by reference, can be modified
    std::cout << a + b << std::endl;
};

// Mixed capture: b by reference, a by value
auto fn4 = [&, a]() {
    // b is captured by reference, can be modified
    // a is captured by value, cannot be modified
    std::cout << a + b << std::endl;
};

Practical Implementation Example

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> values = {1, 2, 3, 4, 5};
    int limit = 3;
    
    // Using lambda as predicate with captured threshold
    auto it = std::find_if(values.begin(), values.end(),
        [limit](int item) {
            return item > limit;
        });
    
    if (it != values.end()) {
        std::cout << "First value exceeding " << limit 
                  << " is " << *it << std::endl;
    }
    
    // Modify vector elements in place
    std::for_each(values.begin(), values.end(),
        [](int& item) {
            item *= 2;
        });
    
    // Display transformed results
    for (const auto& v : values) {
        std::cout << v << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Critical Considerations

Capture by reference creates a dependency on the lifetime of the original variable. Using [&] default capture may inadvertently retain references to objects that have already been destroyed. The mutable keyword only applies to captured-by-value copies. When passing large objects, const reference parameters prevent unnecessary copying while maintaining read-only access to the original data.

Tags: C++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.