简体   繁体   English

为什么lambda删除cv和ref?

[英]Why lambda removes cv and ref?

Given a lambda: 鉴于lambda:

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

Why return type of f is T (not const T& )? 为什么返回f类型是T (不是const T& )? Where is this in the Standard? 这个标准在哪里?

The point is: 重点是:

  1. The use of auto for return type deduction employ template type deduction rule. 使用auto返回类型推导采用模板类型推导规则。
  2. The return type is delcared as passed-by-value; 返回类型被指定为值传递; which means that the reference-ness and top-level cv-qualifiers of expression used for deduction (ie var ) are ignored. 这意味着忽略用于演绎的表达式的引用和顶级cv限定符(即var )。

Quotes from the standard: 标准引用:

About auto : 关于汽车

If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. 如果占位符是自动类型说明符,则使用模板参数推导的规则确定推断类型T'替换T. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initialization is copy-list-initialization, with std::initializer_list. 通过使用新发明的类型模板参数U替换auto的出现来获取P,或者如果初始化是copy-list-initialization,则使用std :: initializer_list替换auto的出现。 Deduce a value for U using the rules of template argument deduction from a function call ([temp.deduct.call]), where P is a function template parameter type and the corresponding argument is e. 使用函数调用中的模板参数推导规则([temp.deduct.call])推导U的值,其中P是函数模板参数类型,相应的参数是e。 If the deduction fails, the declaration is ill-formed. 如果扣除失败,则声明格式不正确。 Otherwise, T' is obtained by substituting the deduced U into P. 否则,通过将推导的U代入P来获得T'。

About the rule of template argument deduction from a function call : 关于函数调用模板参数推导规则:

If P is not a reference type: 如果P不是引用类型:

  • If A is a cv-qualified type, the top-level cv-qualifiers of A's type are ignored for type deduction. 如果A是cv限定类型,则类型推导将忽略A类型的顶级cv限定符。

About reference : 关于参考

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. 如果表达式最初具有“对T的引用”类型([dcl.ref],[dcl.init.ref]),则在进行任何进一步分析之前将类型调整为T.

So for var (ie A ) is const T& , the deduced return type would be T here. 因此对于var (即A )是const T& ,推导出的返回类型将是T

This is a much more fundamental issue about C++. 这是关于C ++的一个更基本的问题。 Nothing to do with specifically with lambdas or auto . 与lambdas或auto无关。

In C++, a reference behaves the same as a non-reference in almost all situations. 在C ++中,几乎在所有情况下,引用的行为与非引用相同。 This is deliberate, and is based on the idea that a reference to an object really should be equivalent to the object itself. 这是故意的,并且基于对对象的引用确实应该等同于对象本身的想法。 In the following code, there is no real difference between x and y : 在以下代码中, xy之间没有真正的区别:

int x  = 3;
int &y = x;

In fact, it is impossible to distinguish them (except via decltype ). 事实上,不可能区分它们(除了通过decltype )。 The are both lvalues of type int . 这两个都是int类型的左值。 If you call foo(x) and foo(y) , the compiler will treat them both as having the same type and value category and therefore the same overload is selected. 如果调用foo(x)foo(y) ,编译器会将它们视为具有相同的类型和值类别,因此选择相同的重载。

I would interpret x and y by saying that both of them are references to the same object. 我会解释xy ,说它们都是对同一个对象的引用。 They are two different 'names' for the same object. 它们是同一对象的两个不同的“名称”。

Therefore return x and return y are equivalent to each other, and therefore the lambda won't care about the & when deducing its return type. 因此, return xreturn y彼此相等,因此lambda在推断其返回类型时不关心&

This explains why the & is ignored. 这解释了为什么&被忽略了。 C++ tries to "ignore" the & as much as possible, precisely in order that references can be treated as fully equivalent to the original object. C ++试图尽可能地“忽略” & ,正是为了使引用可以被视为完全等同于原始对象。

Now that it is established that we are returning by value, not by reference, then we can understand why the const is also ignored. 既然确定我们按值返回,而不是通过引用返回,那么我们就可以理解为什么const也被忽略了。 A copy of an object doesn't need to be const . 对象的副本不需要是const Consider this in reverse also: we can pass a const int argument as an int parameter of a function. 相反也可以这样考虑:我们可以通过一个const int参数作为int函数的参数。

Return type deduction of auto return functions with no trailing return type in C++11 or 14 is done as if you did 在C ++ 11或14中没有尾随返回类型的auto返回函数的返回类型推导就像你所做的那样

auto retval = /* return expression */

and auto always deduces a value type. auto 总是推导出一种值类型。

Lambdas are the only auto return functions in C++11. Lambda是C ++ 11中唯一的auto返回函数。 In C++14 it was extended to other functions (following similar rules). 在C ++ 14中,它被扩展到其他功能(遵循类似的规则)。

In C++11 you can fix this with a ->decltype(var) or a ->int const& trailing return type on your lambda. 在C ++ 11中,你可以在lambda上使用->decltype(var)->int const& trailing return类型来解决这个问题。

In C++14 you can simply ->decltype(auto) , which changes the return type deduction rules from 在C ++ 14中,你可以简单地->decltype(auto) ,它改变了返回类型扣除规则

auto retval = /* return expression */

to be more like 更像是

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

which, in your case, means not dropping the & or the const . 在你的情况下,这意味着不会删除&const

Note that returning const& parameters is exceedingly dangerous, as rvalues can be turned into const& implicitly, and reference lifetime extension does not commute past a function call. 请注意,返回const&参数非常危险,因为rvalues可以变为const& implicit,并且引用生存期扩展不会通过函数调用。 So: 所以:

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

is safe even if some_function() returns a temporary, while assuming f behaves the way you want f to behave: 即使some_function()返回一个临时值,也是安全的,而假设f行为方式与f的行为方式相同:

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

generates x a dangling reference to the temporary returned by some_function() . 生成xsome_function()返回的临时值的悬空引用。

This can break things surprisingly, like the fact that for(:) loops implicitly use auto&& parameters behind the scene, and clean up temporaries between the initialization of the range expression and the execution of the iteration. 这可以令人惊讶地破坏,就像for(:)循环隐式使用场景后面的auto&&参数,并清除范围表达式初始化和迭代执行之间的临时性。

This answer is not yet complete, as I have not added references into the standard. 这个答案还没有完成,因为我没有在标准中添加引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM