Sökresultat för

Använder du lambda-uttryck i C++

4 minuter i lästid
Jens Riboe
Jens Riboe
Senior/Expert Software Developer
Använder du lambda-uttryck i C++

En av de mest uppskattade nyheterna i Modern C++ är utan tvekan lambda-uttryck. När de introducerades i C++11, så innebar det ett stort steg mot mer uttrycksfull och koncis kod. Lambda-uttryck gör det möjligt att deklarera funktioner direkt i kontexten där de används, vilket gör att man slipper definiera hjälpfunktioner på andra ställen i koden. Här nedan kommer jag att gå igenom syntaxen, visa flera exempel, samt peka ut några viktiga fördelar.

En första titt

Låt oss börja med ett enkelt exempel. Vi vill sortera en lista med strängar, men på ett specialanpassat sätt. Med ett lambda-uttryck kan vi skriva:

#include <vector>
#include <string>
#include <algorithm>

int main() {
    auto names = std::vector<std::string>{"Eva", "Anna", "Kalle", "Bo"};

    std::sort(std::begin(names), std::end(names),
              [](std::string const& a, std::string const& b) {
                  return a.size() < b.size();
              });
}

Här definieras en anonym funktion inline, som tar två strängar och jämför deras längd. Funktionen skickas som tredje argument till std::sort. Notera hur mycket smidigare detta är än att behöva skapa en separat jämförelsefunktion någon annanstans i filen.

Syntaxen

Syntaxen för lambda ser ut så här:

[capture](parameter-list) -> return-type { body };

De flesta delar kan utelämnas om de inte behövs. Här är några varianter:

[]() { std::cout << "Hello\n"; }();                     // Ingen parameter
[](int x) { return x * x; };                            // Med parameter
[=](int x) { return x + offset; };                      // Fångar variabler med värde
[&](int x) { total += x; };                             // Fångar variabler med referens

Användning med standardalgoritmer

Lambda-uttryck kommer verkligen till sin rätt när de används tillsammans med algoritmer i STL. Här är ett par exempel:

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

int main() {
    auto values = std::vector<int>{1, 3, 5, 7, 9, 10, 12};

    auto even_count = std::count_if(std::begin(values), std::end(values),
                                    [](int x) { return x % 2 == 0; });

    std::cout << "# even: " << even_count << "\n";
}

Eller varför inte filtrera ut värden:

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

int main() {
    auto values = std::vector<int>{1, 2, 3, 4, 5, 6};
    auto evens  = std::vector<int>{};

    std::copy_if(std::begin(values), std::end(values), std::back_inserter(evens),
                 [](int x) { return x % 2 == 0; });

    for (auto val : evens) std::cout << val << " ";
}

Lambdas som funktionsobjekt

Ibland behöver man spara ett lambda för senare användning. Det går utmärkt:

auto is_positive = [](int x) { return x > 0; };

if (is_positive(42)) std::cout << "Positive\n";

Man kan även skicka lambdas till funktioner som tar std::function:

#include <functional>

void do_twice(const std::function<void()> &f) { f(); f(); }

int main() {
    auto count = 0;
    do_twice( [&]{ count++; } );
    std::cout << count << "\n";  // prints 2
}

Generiska lambdas

Från och med C++14 kan lambdas vara generiska, d.v.s. de kan ta parametrar av vilken typ som helst:

auto print = [](const auto& value) { std::cout << value << "\n"; };

print(42);
print("hej");
print(3.14);

Detta är kraftfullt, särskilt i generisk kod eller när du arbetar med templates.

Mutable lambdas

Som standard är lambdas som fångar variabler med värde ([=]) const, d.v.s. de kan inte ändra de fångade värdena. Om man vill tillåta ändring krävs mutable:

auto counter = [n = 0]() mutable { return ++n; };

std::cout << counter() << counter() << counter() << "\n"; // prints 123

Avslutande reflektioner

Lambda-uttryck har blivit ett oumbärligt verktyg i min C++-verktygslåda. De gör koden kompaktare, tydligare och mer lokalt begriplig. Särskilt i kombination med auto, std::function och ranges (C++20) uppstår en elegant programmeringsstil som jag uppriktigt tycker är både kraftfull och njutbar att använda.

Och precis som alltid i Modern C++, så handlar det om att använda rätt verktyg för rätt situation. Lambda-uttryck är ett av de verktygen som jag gärna sträcker mig efter – ofta.

Min katt tycker också om lambda. Men han använder bara den som liggplats på tangentbordet.