Lambda in C++ 17 : New Updates

Share the Article

Lambda in C++ 17 has new changes in its feature list. They are now better and more suited to fit in more places, like, in constant expressions. The changes are explained below.

By default, Lambda is constexpr

This means any compile-time constant expressions can now use lambda expressions. Such lambda shall be executed by compiler in compile time. However, it should be noted that for C++17 compiler to execute a lambda, firstly, all inputs should be available and secondly, there should be no code which have run-time dependencies. For instance, there should be no virtual functions, no static variables, no try-catch, no new-delete and anything which a compiler can not run.

The following program demonstrates how a lambda is called to assign value to constexpr integer “a”.

#include <iostream> //main header using namespace std; //namespace int main() { auto sqr = [](auto i) { return i*i;}; constexpr int avar = sqr(3); cout << avar << endl; return 0; }

Output

9

The above code shall not compile in C++11 or C++14 and will result in following error.

compiler error with constexpr when calling non-const lambda

Lambda explicitly declared with constexpr keyword

Although lambda by default is constexpr in C++17, however, a program can use them for run-time expressions also. In case, these lambdas are valid for compile-time context, then it is a good practice to use constexpr keyword in the declaration. However, the program can still use them for run-time contexts.

The following example demonstrates how constexpr lamda is evaluating value at run-time.

#include <iostream> //main header using namespace std;//namespace int main() { auto sqr = [](int ivar) constexpr { return ivar*ivar; }; int ivar; cin >> ivar; int avar = sqr(ivar); //Taking non-constant input cout << avar << endl; return 0; }

Output

Lambda of type constexpr in C++17

Advantage of constexpr with lambda declaration

In case, a lambda declaration is having constexpr keyword then it should not use any statement which compiler cannot execute. The compiler cannot accept any statement inside such lambda. The following program uses a “cout” statement inside an explicitly constexpr lambda. The result is compilation error.

#include <iostream> //main header using namespace std; //namespace int main() { auto sqr = [](int i) constexpr { cout << "ABC\n"; return i*i; }; constexpr int avar = sqr(3); cout << avar << endl; return 0; }

Error message is as following.

a constexpr lmbda function having invalid statements in C++17

Lambda inside member functions

When class member functions contains lambda then, by default, they do not have access to “this” pointer. There is always a possibility that a lambda may outlive “this” pointer. Therefore, to use “this” pointer, lambda has to be explicitly capture it.

The following program tries to directly use a this pointer in lambda and therefore fails at compilation.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: void funda() { auto l0 = []() { cout << this << endl; }; l0(); } }; int main() { MainFunda a1; cout << &a1 << endl; a1.funda(); return 0; }

The compiler generates following error message.

Lambda function inside member functions in C++

C++11 way of passing this pointer to lambda

In C++11, the program has to pass “this” pointer either by value or by reference. The method is just like a workaround because, in all cases, this will pass the underlying object as reference. And this reference may become invalid when underlying object destroys, however, it is possible that lambda may outlive the object.

The following program show 3 ways of passing this pointer for capture. From the output, it is clear that ultimately same object is passed and all 3 cases.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda() { cout << "Constructor\n" ; } MainFunda(const MainFunda&&) { cout << "Copy Constructor\n" ; } ~MainFunda() { cout << "Destructor\n" ; } void funda() { auto l1 = [this](){cout << this << endl;}; auto l2 = [=] (){cout << this << endl;}; auto l3 = [&] (){cout << this << endl;}; l1(); l2(); l3(); } }; int main() { MainFunda a1; cout << &a1 << endl; a1.funda(); return 0; }

The output prints exactly same “this” pointer address in all cases.

Lambda function capturing this pointer in C++11

C++14 way of passing this pointer to lambda

C++14 way of passing “this” pointer to lambda is slightly better as it uses generic capture mode. Therefore, ultimately a copy of object goes inside the lambda as capture. However, this method looks very ugly.

The following program shows that on passing “this” pointer, firstly, the corresponding copy-constructor is called. Finally, a new object goes inside lambda capture.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda() { cout << "Constructor\n" ; } MainFunda(const MainFunda& rhs) { cout << "Copy Constructor\n" ; } void funda() { auto l0 = [thiscopy=*this]() { cout << &thiscopy << endl; }; l0(); } ~MainFunda() { cout << "Destructor\n" ; } }; int main() { MainFunda a1; cout << &a1 << endl; a1.funda(); return 0; }

The output shows that address of original object is different than address shown by “this” pointer. Clearly, copy constructor call is also happening during the process of capture.

Lambda function capturing this pointer in C++14

C++17 way of passing this pointer to lambda

C++17 has the cleanest approach. It passes “[*this]” in capture. This syntax causes compiler to first call copy constructor for creating a new object. And then finally, the new object goes inside lambda. The following program demonstrates this new syntax.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda() { cout << "Constructor\n" ; } MainFunda(const MainFunda& rhs) { cout << "Copy Constructor\n" ; } void funda() { auto l0 = [*this]() { cout << this << endl; }; l0(); } ~MainFunda() { cout << "Destructor\n" ; } }; int main() { MainFunda a1; cout << &a1 << endl; a1.funda(); return 0; }

The output shows that just like, the case of C++14, a new object goes inside lambda. Hence, the address of “this” pointer inside lambda is different than address of original object.

Lambda function capturing this pointer in C++17

Main Funda: Lambda in C++ 17 is more clean and can be used in more places.

Related Topics:

Lambda in C++11
Lambda in C++14
What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism
emplace_back vs push_back


Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *