Why does lambda remove cv and ref?

Given lambda:

auto f = [](const T& var){ return var; }; 

Why is the return type f is T (not const T& )? Where is it in the standard?

+5
source share
3 answers

Point:

  • Using auto-return type inference uses the template type inference rule.
  • The return type is divided as passed by value; which means that reference values ​​and top-level cv qualifiers of the expression used for output (i.e. var ) are ignored.

Quotes from the standard:

About auto :

If the placeholder is an automatic type specifier, the inferred type T 'that replaces T is determined using the rules to subtract the template argument. Get P from T by replacing the occurrences of auto with either a new pattern template of type U, or, if initialization is the initialization of the copy list, with std :: initializer_list. Derive the value for U using the rules for deriving the template argument from the function call ([temp.deduct.call]), where P is the type of the function template parameter and the corresponding argument is e. If the deduction is not made, the declaration is poorly formed. Otherwise, T 'is obtained by replacing the deduced U by P.

About the rules, deriving a template argument from a function call :

If P is not a reference type:

  • If A is a type with qualification cv, the top qualifiers of type C for the type of type are ignored to infer the type.

About link :

If the expression is initially of type "reference to T" ([dcl.ref], [dcl.init.ref]), the type is brought to T before any further analysis.

So, for var (i.e. A ) const T& , the return type will be T here.

+5
source

This is a much more fundamental problem with C ++. It has nothing to do with lambdas or auto .

In C ++, a link behaves the same as without a link in almost all situations. This is intentional and based on the idea that a reference to an object should really be equivalent to the object itself. In the following code there is no real difference between x and y :

 int x = 3; int &y = x; 

In fact, they cannot be distinguished (except decltype ). Both are int values. If you call foo(x) and foo(y) , the compiler will treat them as a category of the same type and value, and therefore the same overload will be selected.

I would interpret x and y , saying that they are both references to the same object. These are two different names for the same object.

Therefore, return x and return y equivalent to each other, so lambda will not care about & when returning the return type.

This explains why & ignored. C ++ tries to β€œignore” & as much as possible, precisely so that the links can be considered completely equivalent to the original object.

Now that it is established that we are returning by value, and not by reference, then we can understand why const also ignored. The copy of the object should not be const . Consider also the opposite: we can pass the argument const int as an int parameter for the function.

+4
source

Inference of the return type of auto return functions without a return type of return in C ++ 11 or 14 is performed as if you were doing

 auto retval = /* return expression */ 

and auto always infers the type of value.

Lambdas are the only auto return functions in C ++ 11. In C ++ 14, it was extended to other functions (by similar rules).

In C ++ 11, you can fix this with the return type ->decltype(var) or ->int const& on your lambda.

In C ++ 14, you can simply ->decltype(auto) , which changes the rules for outputting the type of the return type from

 auto retval = /* return expression */ 

more like

 decltype(/* return expression */) retval = /* return expression */ 

which in your case means you are not dropping & or const .

Please note that the returned const& parameters are extremely dangerous, since r values ​​can be overridden in const& implicitly, and the reference extension of the life cycle does not go through a function call. So:

 auto&& x = some_function(); // or int const& x = some_function(); 

is safe even if some_function() returns a temporary value, while f is supposed to behave the way you want f :

 auto&& x = f( some_function() ); // or int const& x = f( some_function() ); 

generates an x dangling reference to a temporary object returned by some_function() .

This is surprisingly surprising, as is the fact that the for(:) loops implicitly use the auto&& parameters behind the scene and clear the time intervals between initializing the range expression and performing the iteration.

This answer is not yet complete, since I have not added a link to the standard.

+2
source

Source: https://habr.com/ru/post/1258592/


All Articles