Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a generic forwarding lambda in C++14?

How do I write a generic forwarding lambda in C++14?

Try #1

[](auto&& x) { return x; }

Inside the function body, x is an lvalue, so this doesn't work.

Try #2

[](auto&& x) { return std::forward<decltype(x)>(x); }

This properly forwards references inside the lambda, but it will always return by value (unless the compiler elides the copy).

Try #3

[](auto&& x) -> decltype(x) { return std::forward<decltype(x)>(x); }

This returns the same type as the argument (probably -> auto&& would work as well) and appears to work properly.

Try #4

[](auto&& x) noexcept -> decltype(x) { return std::forward<decltype(x)>(x); }

Does adding noexcept make this lambda more applicable and thus strictly better than #3?

like image 771
mavam Avatar asked May 07 '15 03:05

mavam


People also ask

What is generic lambda in C++?

Generic lambdas were introduced in C++14 . Simply, the closure type defined by the lambda expression will have a templated call operator rather than the regular, non-template call operator of C++11 's lambdas (of course, when auto appears at least once in the parameter list).

What is generic lambdas?

Conceptually, a generic lambda is equivalent to a function object with a templatized function-call operator method: struct LambdaClosureType { ... template<template-params> ret operator()(params) const { ... } .... };

What is forwarding reference in C++?

When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.

What is C++ perfect forwarding?

What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.


1 Answers

A return type of decltype(x) is insufficient.

Local variables and function parameters taken by value can be implicitly moved into the return value, but not function parameters taken by rvalue reference (x is an lvalue, even though decltype(x) == rvalue reference if you pass an rvalue). The reasoning the committee gave is that they wanted to be certain that when the compiler implicitly moves, no one else could possibly have it. That is why we can move from a prvalue (a temporary, a non-reference qualified return value) and from function-local values. However, someone could do something silly like

std::string str = "Hello, world!";
f(std::move(str));
std::cout << str << '\n';

And the committee didn't want to silently invoke an unsafe move, figuring that they should start out more conservative with this new "move" feature. Note that in C++20, this issue will be resolved, and you can simply do return x and it will do the right thing. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0527r0.html

For a forwarding function, I would attempt to get the noexcept correct. It is easy here since we are just dealing with references (it is unconditionally noexcept). Otherwise, you break code that cares about noexcept.

This makes the final ideal forwarding lambda look as follows:

auto lambda = [](auto && x) noexcept -> auto && { return std::forward<decltype(x)>(x); };

A return type of decltype(auto) will do the same thing here, but auto && better documents that this function always returns a reference. It also avoids mentioning the variable name again, which I suppose makes it slightly easier to rename the variable.

As of C++17, the forwarding lambda is also implicitly constexpr where possible.

like image 76
David Stone Avatar answered Oct 18 '22 04:10

David Stone