为什么这个函数返回一个左值引用给定右值参数? [英] Why does this function return an lvalue reference given rvalue arguments?

查看:154
本文介绍了为什么这个函数返回一个左值引用给定右值参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下定义 min 函数

 类型名称T,类型名称U> 
constexpr auto
min(T& t,U&& u) - > decltype(t {
return t& u? t:u;
}

有一个问题:看起来写入

  min(10,20)= 0; 

已使用Clang 3.5和g ++ 4.9测试。



解决方案很简单,只需使用 std :: forward 来恢复参数的rvalue-ness,即修改主体和 decltype

  t< u? std :: forward< T>(t):std :: forward U($)



<但是,我无法解释为什么第一个定义不会产生错误。






鉴于我对转发和通用引用的理解, t u 将他们的参数类型推导为 int&&&< / code>时传递整数文字。但是,在 min 的正文中,参数具有名称,因此它们是左值。现在,条件运算符的真正复杂规则开始运作,但我认为相关行是:



  • E2 [和] E3相同类型的glvalues。在这种情况下,结果具有相同的类型和值类别。


运算符?:应该也是 int&&& 但是,(我可以告诉)Clang和g ++都有 min(int&&& amp;& amp;)返回一个左值引用



显然,我的理解有一个差距,但我不确定什么是我失踪。



正如Niall正确指出的,这里的问题不是使用条件运算符(返回类型 int&&< / code的左值>如预期),但使用 decltype 。如果表达式的值类别为lvalue,则 decltype 的规则为


decltype指定T&


,因此函数的返回值变为 int&& & ,这与C ++ 11的参考折叠规则变成了平凡的 int& 但是如果我们使用 std :: forward ,那么我们可以使用

我们将 operator?:(back)的第二个和第三个参数转换为右值 - 特别是xvalues。因为xvalues仍然是glvalues(你是否保持在后面?),同样的条件运算符规则适用,我们得到相同类型和值类别的结果:即 int&&



现在,当函数返回时,它会触发一个不同的 decltype rule:


如果expression的值类别为xvalue,则decltype指定T&&< / p>

这次,参考折叠给我们 int&& &&& = int&&& ,更重要的是,函数返回xvalue。

解决方案

问题可能在于

code> decltype()
规则。



如果删除了尾部返回类型

  template< typename T,typename U> 
constexpr auto
min(T& t,U&& u)
{
return t& u? t:u;
}

并且允许编译器推导出来,返回类型为 int



使用decltype




...

如果表达式的值类别为lvalue,则decltype指定T&




取自 cppreference



由于涉及 t u 的表达式是一个左值(它们是左值 - 命名的r值引用),返回是左值引用。



在这种情况下,它会导致潜在的情况,使用通用参考时,需要谨慎使用转发(或转发参考)和相关的参考折叠规则。



正如您已经注意到的,为了纠正这种情况,正确使用 std :: forward






有关 std :: forward 和参考折叠可以找到这里是SO


The following definition of a min function

template <typename T, typename U>
constexpr auto
min(T&& t, U&& u) -> decltype(t < u ? t : u)
{
    return t < u ? t : u;
}

has a problem: it seems that it's perfectly legal to write

min(10, 20) = 0;

This has been tested with Clang 3.5 and g++ 4.9.

The solution is straightforward, just use std::forward to restore the "rvalue-ness" of the arguments, i.e. modify the body and the decltype to say

t < u ? std::forward<T>(t) : std::forward<U>(u)

However, I'm at a loss to explain why the first definition doesn't generate an error.


Given my understanding of forwarding and universal references, both t and u deduce their argument types as int&& when passed integer literals. However, within the body of min, the arguments have names, so they are lvalues. Now, the really complicated rules for the conditional operator come into play, but I think the pertinent line is:

  • Both E2 [and] E3 are glvalues of the same type. In this case, the result has the same type and value category.

and therefore the return type of operator?: should be int&& as well, should it not? However, (as far as I can tell) both Clang and g++ have min(int&&, int&&) returning an lvalue reference int&, thus allowing me to assign to the result.

Clearly there is a gap in my understanding, but I'm not sure exactly what it is that I'm missing. Can anybody explain to me exactly what's going on here?


EDIT:

As Niall correctly points out, the problem here is not with the conditional operator (which is returning an lvalue of type int&& as expected), but with the decltype. The rules for decltype say

if the value category of expression is lvalue, then the decltype specifies T&

so the return value of the function becomes int&& &, which by the reference collapsing rules of C++11 turns into plain int& (contrary to my expected int&&).

But if we use std::forward, we turn the second and third arguments of operator?: (back) into rvalues - specifically, xvalues. Since xvalues are still glvalues (are you keeping up at the back?), the same conditional operator rule applies, and we get a result of the same type and value category: that is, an int&& which is an xvalue.

Now, when the function returns, it triggers a different decltype rule:

if the value category of expression is xvalue, then the decltype specifies T&&

This time, the reference collapsing gives us int&& && = int&& and, more importantly, the function returns an xvalue. This makes it illegal to assign to the return value, just as we'd like.

解决方案

The issue may be with the decltype() rules.

This is hinted at; if the trailing return type is removed

template <typename T, typename U>
constexpr auto
min(T&& t, U&& u)
{
    return t < u ? t : u;
}

and the compiler is allowed to deduce it, the return type is int.

Use of decltype

decltype ( expression )
...
if the value category of expression is lvalue, then the decltype specifies T&

Taken from cppreference.

Since the expression involving t and u is an lvalue (they are lvalues - named r-value references), the return is the lvalue reference.

In this situation it leads to a potential situation where a literal could be modified. Careful use of forwarding needs to be applied when using "universal references" (or "forwarding references") and the associated reference collapsing rules.

As you have already noted, to correct the situation, the correct use of std::forward needs to be applied and the return type will be the expected one.


For further details on std::forward and reference collapsing can be found here on SO.

这篇关于为什么这个函数返回一个左值引用给定右值参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆