[英]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: 重点是:
var
) are ignored. var
)。 Quotes from the standard: 标准引用:
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限定符。
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
: 在以下代码中,
x
和y
之间没有真正的区别:
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. 我会解释
x
和y
,说它们都是对同一个对象的引用。 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 x
和return 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()
. 生成
x
对some_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.