
#C++ Lambda Functions Usage
As explained in this C++ Reference document, a C++ Lambda function constructs a closure. Using lambda functions, it is possible to store a function together with its environment.
#include "stdafx.h"
#include <iostream>
int main()
{
auto isEven = [ & ]( const int value )->bool
{
return value % 2 == 0;
};
std::cout << "Is 11 an even number? "
<< ( isEven( 11 ) ? "TRUE" : "FALSE" )
<< std::endl;
return 0;
}
Is 11 an even number? FALSE
Press any key to continue . . .
The [] characters define the captured variables. These variables can be accessed from the lambda functions as copies of the original value or by reference (using & character). These variables are optional and they can be defined in any order.
#1) Initialize a class member variable
#include "stdafx.h"
#include <iostream>
#include <vector>
// lambda function used to initialize a vector
auto setValues = []()->std::vector<int>
{
std::vector<int> tmp{};
for (int i = 0; i < 10; i++)
{
tmp.emplace_back(i);
}
return tmp;
};
class SomeClass
{
public:
SomeClass()
: m_data(setValues()) {}
void print()
{
for (int v : m_data)
{
std::cout << v << std::endl;
}
}
private:
const std::vector<int> m_data;
};
int main()
{
SomeClass myClass; // create a class instance
myClass.print(); // print vector values
return 0;
}
0
1
2
3
4
5
6
7
8
9
Press any key to continue . . .
#2) Call a member function of class 2 from inside the member function of the class 1
#include "stdafx.h"
#include <iostream>
#include <set>
#include <string>
#include <functional>
struct Item
{
int num;
std::string msg;
};
class OtherFilterManager
{
public:
OtherFilterManager()
: m_validInt{1,2,3,4,5}
{
}
bool pass(const Item& el)
{
return m_validInt.end() != m_validInt.find(el.num);
}
private:
const std::set<int> m_validInt;
};
class FilterManager
{
public:
void printMsgIfCheckPasses(
const Item& el,
const std::function<bool(const Item& obj)>& passFn
)
{
if (!el.msg.empty())
{
if (passFn(el))
{
std::cout << el.msg << std::endl;
}
else
{
std::cout << "Cannot print msg since "
"other check failed" << std::endl;
}
}
else
{
std::cout << "Message is empty" << std::endl;
}
}
};
int main()
{
Item invalidItem{ 100, "This message is not "
"printed because 100 is invalid"};
Item invalidItem2{ 1, "" }; // Empty message
// cannot be printed
Item validItem{ 2, "This message is printed" };
FilterManager fltMngr1; // first filter manager
OtherFilterManager fltMngr2; // second filter manager
// Lambda function
auto checkSecondFilter = [&fltMngr2](const Item& obj)
{
/*
Also additional tasks can be executed
in this lambda function before checking
the additional type of filters,
e.g., increase a counter parameter.
*/
return fltMngr2.pass(obj);
};
// Checking this item cannot print any message
fltMngr1.printMsgIfCheckPasses(
invalidItem,
checkSecondFilter
);
// Checking this item cannot print any message
fltMngr1.printMsgIfCheckPasses(
invalidItem2,
checkSecondFilter
);
// Checking this item can print a message
fltMngr1.printMsgIfCheckPasses(
validItem,
checkSecondFilter
);
return 0;
}
Cannot print msg since other check failed
Message is empty
This message is printed
Press any key to continue . . .
In addition, using the lambda function, the “FilterManager” class does not need to know about the existence of the class “OtherFilterManager”. Also, the lambda function can capture local variables. In this way, the behavior of the lambda function depends on the values of the captured variables too.
#3) Define custom tasks
#include "stdafx.h"
#include <iostream>
#include <set>
#include <string>
#include <functional>
struct Item
{
int num;
std::string msg;
};
class OtherFilterManager
{
public:
OtherFilterManager()
: m_validInt{1,2,3,4,5}
{
}
bool pass(const Item& el)
{
return m_validInt.end() != m_validInt.find(el.num);
}
private:
const std::set<int> m_validInt;
};
class FilterManager2
{
public:
FilterManager2() {}
void printMsgIfCheckPasses(
const Item& el,
const std::function<void(const Item& obj)>& passFn,
const std::function<void(const Item& obj,
const std::string& msg
)>& rejectFn
)
{
std::string myMsgOnFailure{
"Custom message on Failure!" };
if (!el.msg.empty())
{
passFn(el);
}
else
{
rejectFn(el, myMsgOnFailure );
}
}
};
int main()
{
Item invalidItem{ 1, "" }; // Empty message
// cannot be printed
Item validItem{ 2, "This message is printed" };
OtherFilterManager fltMngr;
FilterManager2 fltMngr2;
auto checkIntegerValidity = [&fltMngr2](const Item& obj)
{
if (fltMngr2.pass(obj))
std::cout << "Valid integer ( "<< obj.num << " )" << std::endl;
};
bool printNumber = false;
auto onReject = [&](const Item& obj,
const std::string& msgToPrint)
{
std::cout << msgToPrint << std::endl;
if (printNumber)
{
std::cout << "Integer "
"is: " << obj.num << std::endl;
}
else // printNumber is false
{
std::cout << "Integer "
"is not printed." << std::endl;
}
};
// Checking validity of this item
fltMngr2.printMsgIfCheckPasses(
invalidItem,
checkIntegerValidity,
onReject
);
// Checking validity of this item
fltMngr2.printMsgIfCheckPasses(
validItem,
checkIntegerValidity,
onReject
);
return 0;
}
Custom message on Failure!
Integer is not printed.
Valid integer ( 2 )
Press any key to continue . . .
class B
{
public:
// ... other stuff
void incrementValueAtIndex(const int index);
void decrementValueAtIndex(const int index);
};
B instance1;
// my great lambda function :)
auto myLambda = [&](const bool valid,
const int privateDataFromA )
{
if (valid)
{
instance1.incrementValueAtIndex(privateDataFromA);
}
else
{
instance1.decrementValueAtIndex(privateDataFromA);
}
};
class A
{
// Other stuff
// This function is called when an item is added
void onAddItem(
const Item& el,
const std::function<void(const bool valid, const int index)>& myFn
)
{
/*
Other stuff...
*/
myFn( !el.msg.empty(), el.num );
}
};
- if no copy occurs, there is no loss of performance
- since data is defined in one place only, then there is no risk of data inconsistency.
- the code design is simpler and neater.
#Final thoughts
In this article, three different examples have been proposed to show some Lambda functions usage. In C++, the Lambda functions are a sort of Swiss Army knife. They can be used in many situations to perform any kind of action, e.g., to access some data that are private to a specific class only and process them in other parts of the code ensuring data consistency and avoiding any loss of performance.