Examples illustrate the use of C++ expressions in C++ programming

  • 2020-05-07 20:13:29
  • OfStack

function object and Lambdas
When you write code, especially with the STL algorithm, you might use function Pointers and function objects to solve problems and perform calculations. Function Pointers and function objects have their pros and cons. For example, a function pointer has the lowest syntax overhead but does not maintain state within the scope, and a function object can maintain state but requires the syntax overhead of a class definition.
lambda combines the advantages of function Pointers and function objects and avoids their disadvantages. lambda is similar to function objects in that it is flexible and can maintain state, but the difference is that its concise syntax does not require explicit class definitions. With lambda, you can write code that is less complex and less error-prone than the equivalent function object code.
The following example compares the use of lambda with that of function objects. The first example USES lambda to print to the console whether each element in the vector object is even or odd. The second example USES a function object to do the same thing.
Example 1: use lambda
This example passes one lambda to the for_each function. The lambda prints a result that indicates whether each element in the vector object is even or odd.
code


// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
 // Create a vector object that contains 10 elements.
 vector<int> v;
 for (int i = 1; i < 10; ++i) {
  v.push_back(i);
 }

 // Count the number of even numbers in the vector by 
 // using the for_each function and a lambda.
 int evenCount = 0;
 for_each(v.begin(), v.end(), [&evenCount] (int n) {
  cout << n;
  if (n % 2 == 0) {
   cout << "is even" << endl;
   ++evenCount;
  } else {
   cout << "is odd" << endl;
  }
 });

 // Print the count of even numbers to the console.
 cout << "There are " << evenCount 
  << " even numbers in the vector." << endl;
}

The output


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

notation
In this example, the third argument to the for_each function is 1 lambda. The [&evenCount] section specifies the capture clause of the expression, (int n) specifies the parameter list, and the rest specifies the body of the expression.
Example 2: using function objects
Sometimes lambda is too large to scale substantially from the previous example. The next example USES the function object (instead of lambda) and the for_each function to produce the same results as in example 1. Both examples store the number of even Numbers in the vector object. To keep the state of the operation, the FunctorClass class stores the m_evenCount variable as a member variable by reference. To perform this operation, FunctorClass implements the function call operator operator(). The code generated by the Visual C++ compiler is similar in size and performance to the lambda code in example 1. For basic problems like the examples in this article, the simpler lambda design may be superior to the function object design. However, if you think that this functionality might need to be significantly extended in the future, use the function object design to make code maintenance easier.
For more information about operator(), see the function call (C++).

code


// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

class FunctorClass
{
public:
 // The required constructor for this example.
 explicit FunctorClass(int& evenCount)
  : m_evenCount(evenCount) { }

 // The function-call operator prints whether the number is
 // even or odd. If the number is even, this method updates
 // the counter.
 void operator()(int n) const {
  cout << n;

  if (n % 2 == 0) {
   cout << " is even " << endl;
   ++m_evenCount;
  } else {
   cout << " is odd " << endl;
  }
 }

private:
 // Default assignment operator to silence warning C4512.
 FunctorClass& operator=(const FunctorClass&);

 int& m_evenCount; // the number of even variables in the vector.
};


int main()
{
 // Create a vector object that contains 10 elements.
 vector<int> v;
 for (int i = 1; i < 10; ++i) {
  v.push_back(i);
 }

 // Count the number of even numbers in the vector by 
 // using the for_each function and a function object.
 int evenCount = 0;
 for_each(v.begin(), v.end(), FunctorClass(evenCount));

 // Print the count of even numbers to the console.
 cout << "There are " << evenCount
  << " even numbers in the vector." << endl;
}

The output


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.


declares Lambda expressions
example 1
Since the lambda expression is typed, you can assign it to the auto variable or function object, as shown below:
code


// declaring_lambda_expressions1.cpp
// compile with: /EHsc /W4
#include <functional>
#include <iostream>

int main()
{

 using namespace std;

 // Assign the lambda expression that adds two numbers to an auto variable.
 auto f1 = [](int x, int y) { return x + y; };

 cout << f1(2, 3) << endl;

 // Assign the same lambda expression to a function object.
 function<int(int, int)> f2 = [](int x, int y) { return x + y; };

 cout << f2(3, 4) << endl;
}

The output


5
7

note
While lambda expressions are mostly declared in the body of a function, they can be declared anywhere a variable is initialized.
Example 2
The Visual C++ compiler binds the Visual expression to the captured variable when it is declared instead of calling the lambda expression. The following example shows an lambda expression that captures the local variable i by value and local variable j by reference. Since the lambda expression captures i by value, reassigning i later in the program does not affect the result of the expression. However, since the lambda expression captures j by reference, reassigning j affects the result of that expression.
code


// declaring_lambda_expressions2.cpp
// compile with: /EHsc /W4
#include <functional>
#include <iostream>

int main()
{
 using namespace std;

 int i = 3;
 int j = 5;

 // The following lambda expression captures i by value and
 // j by reference.
 function<int (void)> f = [i, &j] { return i + j; };

 // Change the values of i and j.
 i = 22;
 j = 44;

 // Call f and print its result.
 cout << f() << endl;
}

The output


47

calls the Lambda expression
you can immediately call the lambda expression, as shown in the following code snippet. The second code snippet demonstrates how to pass lambda as a parameter to the standard template library (STL) algorithm, such as find_if.
Example 1
The lambda expression declared by the following example returns the sum of two integers and is immediately invoked using arguments 5 and 4:
code


// calling_lambda_expressions1.cpp
// compile with: /EHsc
#include <iostream>

int main()
{
 using namespace std;
 int n = [] (int x, int y) { return x + y; }(5, 4);
 cout << n << endl;
}

The output

9

Example 2
The following example passes the lambda expression as an argument to the find_if function. If the lambda expression takes an even number of arguments, true is returned.
code


// calling_lambda_expressions2.cpp
// compile with: /EHsc /W4
#include <list>
#include <algorithm>
#include <iostream>

int main()
{
 using namespace std;

 // Create a list of integers with a few initial elements.
 list<int> numbers;
 numbers.push_back(13);
 numbers.push_back(17);
 numbers.push_back(42);
 numbers.push_back(46);
 numbers.push_back(99);

 // Use the find_if function and a lambda expression to find the 
 // first even number in the list.
 const list<int>::const_iterator result = 
  find_if(numbers.begin(), numbers.end(),[](int n) { return (n % 2) == 0; });

 // Print the result.
 if (result != numbers.end()) {
  cout << "The first even number in the list is " << *result << "." << endl;
 } else {
  cout << "The list contains no even numbers." << endl;
 }
}

The output


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

0

nested Lambda expressions
sample
You can nest the lambda expression in another 1, as shown in the following example. The internal lambda expression multiplies its argument by 2 and returns the result. The external lambda expression invokes the internal lambda expression with its arguments and adds 3 to the result.
code


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

1

The output

13

note
In this example, [](int y) {return y * 2; } is a nested lambda expression.
higher order Lambda function
sample
Many programming languages support the concept of higher-order functions. A higher-order function is an lambda expression that takes another lambda expression as its argument or returns an lambda expression. You can use the function class to make the C++ lambda expression behave like a higher-order function. The following example shows an lambda expression that returns an function object and an lambda expression that takes an function object as its argument.
code

1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

2

The output

30

USES the Lambda expression in the function
sample
You can use the lambda expression in the body of a function. The lambda expression can access any function or data member that the enclosing function can access. You can explicitly or implicitly capture the this pointer to provide access to functions and data members of closed classes.
You can explicitly use the this pointer in a function, as shown below:

void ApplyScale(const vector<int>& v) const
{
 for_each(v.begin(), v.end(), 
  [this](int n) { cout << n * _scale << endl; });
}

You can also implicitly capture the this pointer:


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

4

The following example shows the Scale class that encapsulates decimal values.


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

5

The output


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

6

note
The ApplyScale function USES the lambda expression to print the product of a decimal number with each element in the vector object. The lambda expression implicitly captures the this pointer to access the _scale member.


works with Lambda expressions and templates
sample
Since the lambda expression is typed, you can use it with C++ template 1. The following example shows the negate_all and print_all functions. The negate_all function applies 1 element operator- to each element in the vector object. The print_all function prints each element in the vector object to the console.
code


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

7

The output


34
-43
56
After negate_all():
-34
43
-56

handles exceptions
sample
The body of the lambda expression follows the principles of structured exception handling (SEH) and C++ exception handling. You can handle exceptions thrown in the lambda expression body or defer exception handling to a closed scope. The following example populates the value of one vector object into another using the for_each function and the lambda expression. It USES the try/catch block to handle invalid access to the first vector.
code


1 is even

2 is odd

3 is even

4 is odd

5 is even

6 is odd

7 is even

8 is odd

9 is even

There are 4 even numbers in the vector.

9

The output


Caught 'invalid vector<T> subscript'.

note
For more information on exception handling, see exception handling in Visual C++.

works with Lambda expressions and managed types (C++/CLI)
sample
The capture clause of an lambda expression cannot contain a variable with a managed type. However, you can pass the actual arguments with the managed type into the formal argument list of the lambda expression. The following example contains an lambda expression that captures the local unmanaged variable ch by value and takes the System.String object as its parameter.
code


// managed_lambda_expression.cpp
// compile with: /clr
using namespace System;

int main()
{
 char ch = '!'; // a local unmanaged variable

 // The following lambda expression captures local variables
 // by value and takes a managed String object as its parameter.
 [=](String ^s) { 
  Console::WriteLine(s + Convert::ToChar(ch)); 
 }("Hello");
}

The output


Hello!


Related articles: