Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the usage of lambda trailing return type auto?

What is the usage of adding -> auto in []() -> auto { return 4; }?

For me - it is not different than []() { return 4; }

like image 965
PiotrNycz Avatar asked Aug 29 '18 12:08

PiotrNycz


People also ask

What is lambda return type?

The return type of a lambda expression is automatically deduced. You don't have to use the auto keyword unless you specify a trailing-return-type. The trailing-return-type resembles the return-type part of an ordinary function or member function.

Can lambda have a return type?

The return type for a lambda is specified using a C++ feature named 'trailing return type'. This specification is optional. Without the trailing return type, the return type of the underlying function is effectively 'auto', and it is deduced from the type of the expressions in the body's return statements.

What is trailing return type C++?

The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments.

What is the use of lambda functions in C++?

C++ Lambda expression allows us to define anonymous function objects (functors) which can either be used inline or passed as an argument. Lambda expression was introduced in C++11 for creating anonymous functors in a more convenient and concise way.


1 Answers

It is auto by default. The Standard, [expr.prim.lambda]/4, reads:

If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). The lambda return type is auto, which is replaced by the trailing-return-type if provided and/or deduced from return statements as described in [dcl.spec.auto].

My addition.

So, -> auto itself is not useful. However, we can form other return types with auto, namely: -> auto&, -> const auto&, -> auto&&, -> decltype(auto). Standard rules of return type deduction apply. One should bear in mind that auto is never deduced to be a reference type, so by default a lambda returns a non-reference type.

A few (trivial) examples:

// 1.
int foo(int);
int& foo(char);

int x;

auto lambda1 = [](auto& x) { return x; };
static_assert(std::is_same_v<decltype(lambda1(x)), int>);

auto lambda2 = [](auto& x) -> auto& { return x; };
static_assert(std::is_same_v<decltype(lambda2(x)), int&>);

// 2.
auto lambda3 = [](auto x) { return foo(x); };
static_assert(std::is_same_v<decltype(lambda3(1)), int>);
static_assert(std::is_same_v<decltype(lambda3('a')), int>);

auto lambda4 = [](auto x) -> decltype(auto) { return foo(x); };
static_assert(std::is_same_v<decltype(lambda4(1)), int>);
static_assert(std::is_same_v<decltype(lambda4('a')), int&>);

// 3.
auto lambda5 = [](auto&& x) -> auto&& { return std::forward<decltype(x)>(x); };
static_assert(std::is_same_v<decltype(lambda5(x)), int&>);
static_assert(std::is_same_v<decltype(lambda5(foo(1))), int&&>);
static_assert(std::is_same_v<decltype(lambda5(foo('a'))), int&>);

PiotrNycz's addition. As pointed out in comments (credit to @StoryTeller) - the real usage is version with auto& and const auto& and "The degenerate case is just not something worth bending backwards to disallow."

See:

int p = 7;
auto p_cr = [&]()  -> const auto& { return p; };
auto p_r = [&]()  -> auto& { return p; };
auto p_v = [&]()  { return p; }; 

const auto& p_cr1 = p_v(); // const ref to copy of p
const auto& p_cr2 = p_cr(); // const ref to p
p_r() = 9; // we change p here

std::cout << p_cr1 << "!=" << p_cr2 << "!\n";
// print 7 != 9 !
like image 118
Evg Avatar answered Sep 20 '22 14:09

Evg