【问题标题】:Classes, Rvalues and Rvalue References类、右值和右值引用
【发布时间】:2011-07-13 02:43:21
【问题描述】:

左值是绑定到确定的内存区域的值,而右值是一个表达式值,它的存在是暂时的,不一定指向确定的内存区域。每当在需要右值的位置使用左值时,编译器都会执行左值到右值的转换,然后继续求值。

http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues

每当我们构造一个临时(匿名)类对象或从函数返回一个临时类对象时,虽然该对象是临时的,但它是可寻址的。但是,该对象仍然是有效的右值。这意味着该对象是 a) 可寻址的右值或 b) 当编译器期望使用左值时,它正在从左值隐式转换为右值。

例如:

class A
{
public:
    int x;
    A(int a) { x = a; std::cout << "int conversion ctor\n"; }
    A(A&) { std::cout << "lvalue copy ctor\n"; }
    A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a) 
{
    return a;
}

int main(void)
{
    &A(5); // A(5) is an addressable object
    A&& rvalue = A(5); // A(5) is also an rvalue
}

我们还知道函数返回(在以下情况下为a)的临时对象是左值,就像这段代码一样:

int main(void)
{
    ret_a(A(5));
}

产生以下输出:

int conversion ctor

lvalue copy ctor

表示使用实际参数A(5) 调用函数ret_a 调用转换构造函数A::A(int),该构造函数构造函数的形式参数a,值为5。

当函数完成执行时,它会使用a 作为其参数构造一个临时的A 对象,该对象调用A::A(A&amp;)。但是,如果我们要从重载构造函数列表中删除 A::A(A&amp;),返回的临时对象仍然会匹配右值引用构造函数 A::A(A&amp;&amp;)

这是我不太明白的:a 对象如何同时匹配右值引用和左值引用?很明显,A::A(A&amp;)A::A(A&amp;&amp;) 匹配更好(因此a 必须是左值)。但是,由于不能将右值引用初始化为左值,鉴于形式参数a 是左值,它应该无法匹配对A::A(A&amp;&amp;) 的调用。如果编译器正在进行左值到右值的转换,那将是微不足道的。事实上,从 'A' 到 'A&' 的转换也是微不足道的,两个函数应该具有相同的隐式转换序列等级,因此,当 A::A(A&amp;) 和 @987654344 时,编译器应该无法推断出最佳匹配函数@ 在重载函数候选集中。

此外,问题(我之前问过的)是:

给定对象如何同时匹配右值引用和左值引用?

【问题讨论】:

  • “左值”和“右值”指的是表达式,而不是对象。
  • 谢谢。我想问题是如何将 a 对象绑定到右值和左值引用?另外:“int n = 3; n 是一个引用 int 对象的表达式。表达式 n 是一个左值。” eetimes.com/discussion/programming-pointers/4023341/… 如果这是正确的,那么a 对象(上图)在初始化时不会被视为本身的表达式吗?

标签: c++ reference rvalue-reference rvalue lvalue


【解决方案1】:

对我来说:

int main(void)
{
    ret_a(A(5));
}

产量:

int conversion ctor
rvalue copy ctor

(即右值,而不是左值)。这是您的编译器中的一个错误。然而,这是可以理解的,因为这种行为的规则仅在几个月前(2010 年 11 月)发生了变化。更多内容请参见下文。

当函数完成执行时, 然后它构造一个临时的A 使用a 作为参数的对象,其中 调用A::A(A&amp;)

其实没有。当函数ret_a 完成执行时,它会使用a 作为其参数构造一个临时的A 对象,该对象调用A:A(A&amp;&amp;)。这是由于 [class.copy]/p33]1:

当一个省略的标准 满足或将满足复制操作 除了来源 object 是一个函数参数,并且 指定要复制的对象 通过左值,重载决议到 选择副本的构造函数是 第一次执行好像对象是 由右值指定。如果过载 解决失败,或者如果类型 选中的第一个参数 构造函数不是右值引用 对象的类型(可能 cv 限定),重载决议是 再次执行,考虑到 对象作为左值。 [注:这 两级过载解决方案必须是 无论是否复制都执行 会发生省略。它决定了 如果省略,则调用构造函数 未执行,并且选定的 构造函数必须是可访问的,即使 呼叫被省略。 ——尾注]

但是,如果您删除 A::A(A&amp;&amp;) 构造函数,则将选择 A::A(&amp;) 作为返回值。尽管在这种情况下,参数a 的构造将失败,因为您不能使用右值构造它。但是暂时忽略这一点,我相信您的最终问题是:

给定对象如何同时匹配 右值引用和左值 参考?

在引用语句中:

return a;

答案在上面引用的标准草案段落中:尝试第一次重载解析,好像a 是一个右值。如果失败,则使用a 作为左值再次尝试重载解析。这种两阶段过程仅在允许复制省略的上下文中尝试(例如返回语句)。

C++0x 草案最近刚刚更改为在返回按值传递的参数时允许两阶段重载解决过程(如您的示例中所示)。这就是我们看到的不同编译器行为不同的原因。

【讨论】:

  • @Howard 非常感谢您的洞察力。但是不,我最初的问题中没有类型-o。调用调用A::A(A&amp;)。您使用的是哪个编译器?我正在使用 MSVC++
  • 什么是“或者除了源对象是函数参数之外会遇到”是什么意思?
  • @No One:看起来 [class.copy]/p33 还没有在您的编译器中实现。我有点惊讶,因为该特定规则相当古老(大约 4 年)。您的编译器可能有可用的升级来修复此错误?
  • @Howard 我正在使用最新版本的 MSVC++ (2010)
  • @Everyone:我把这个叫做“相当古老的规则”是个错误。事实上,这种行为最近已被标准强制执行。它是 cwg 1148。我没有意识到 return 语句正在返回一个参数。是的,正确答案是“rvalue copy ctor”。然而,直到 2010 年 11 月,正确答案是“lvalue copy ctor”。很抱歉给大家带来了困惑。
猜你喜欢
  • 2012-10-07
  • 1970-01-01
  • 2019-10-12
  • 2016-04-26
  • 2011-02-14
  • 2020-08-24
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多