Lambda Closures in C++

A closure is a concept of functional programming languages. C++ is a multi-paradigm language which offers features of different programming paradigms. But C++ isn’t a real functional language. There are huge differences between a pure functional language and C++. Most implementations done with C++ are based on the procedural paradigm or the object-oriented paradigm. But of course, there are functional programming techniques which can be used in procedural or object-oriented environments too. If these techniques are used wisely, we can benefit by the advantages of the different programming paradigms. On the other hand, if these techniques are used in the wrong way, the resulting implementation can be a ragbag of the disadvantages of the different paradigms.

Within this article I want to explain how lambdas are used to implement closures. And I want to show a way to use this functional feature in mainly non-functional applications, e.g. in applications based on the object-oriented paradigm.

 

Lambda expression

A lambda expression is a mechanism for specifying a function. The primary use for a lambda is to specify a simple action. A lambda expression can be implemented directly as anonymous function, for example within an algorithm method, or it can be named and used by its name. Please read this article for a base explanation of lambda expressions and their alternatives like functors.

From a software design point of view, the main difference between a normal function and a lambda is the scope. Normal functions have access to the variables defined within their own scope. But functions created by lambda expression have access to their own scope and access to the parent scope where they are created.

The following implementation shows an according example. We want to write a function which increases all elements of an array by a variable addend. This can be implemented by using the STL algorithm to transform. We can pass a transformation function to this algorithm. In this kind of implementation, the transformation function needs access to the parent scope variable for the addend. So, we can implement an according object to store this variable, e.g. a functor, or we can use a lambda expression which has access to the parent scope.

void IncreaseAllElements(std::vector& data, const int addend)
{
  auto Sum = [=](int element) {return element + addend; };

  std::transform(data.begin(), data.end(), data.begin(), Sum);
}

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector {-5, 1, 15};

  IncreaseAllElements(data, 20);

  return 0;
}

 

This example shows a very nice way to implement this simple use case. The code is easy to understand and to maintain. We have used a functional programming feature maybe without knowing anything about functional programming or closures. That’s fine in these kinds of implementations. But it can lead to some trouble in case the lambda expression and/or the parent functions will become more complex. In such situations, you may think about alternatives to the lambda expression. Within a previously article I wrote about the possible disadvantages and issues of using closures in procedural implemented applications.

Closure

Now we want to leave the procedural paradigm and implement in a more functional way. Within such a functional context we will see the real power of closures. So, let’s start with a more detailed definition and a technical way of looking at lambda expressions and closure.

A Lambda expression is an expression and as such it exists in source code only. At runtime, an object is generated based on the lambda expression. This object is called closure. A closure of a lambda is therefore something like an instance of a class. The closure object contains the functionality of the lambda expression and it contains the scope of the function where the lambda was created. Therefore, it can have access to the variables of this parent scope.

If we use the closure in the procedural way, like shown in above example, the closure will be destroyed directly at the end of the statement. But if we implement in a functional way, we explicitly want to create and use the closure. Typically, in such functional kind of implementations, the parent function containing the lambda expression is used will return a function. The client will store this function reference and use it several times. The following source code shows the implementation of the above example in a functional way.

std::function CreateVectorAccumulator(const int addend)
{
  return [=](int element) {return element + addend; };  
}

int _tmain(int argc, _TCHAR* argv[])
{
  auto data = std::vector {-5, 1, 15};

  auto accumulator = CreateVectorAccumulator(20);

  std::transform(data.begin(), data.end(), data.begin(), accumulator);
  std::transform(data.begin(), data.end(), data.begin(), accumulator);
  std::transform(data.begin(), data.end(), data.begin(), accumulator);

  return 0;
}

 

This short example shows the power of closures. Of course, you can implement the same functionality without lambda expression, but this will result in a more complex implementation.

In the linked article, I wrote: “… some see this feature as a must have while others see it as a way to write obscure and error-prone code.” And this article further strengthens this idea. If you implement according to functional ideas, closures are really a must have as they will increase the code quality. But if you implement procedural or object-oriented, closures tend to drift of in the opposite direction and their disadvantages will outdo their advantages. Therefore, please use this feature, but do not overuse it.

Werbung
Dieser Beitrag wurde unter C++ veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s