类、右值和右值引用

发布于 2024-10-21 07:58:49 字数 1840 浏览 4 评论 0原文

左值是绑定到确定的内存区域的值,而右值是其存在是临时的并且不一定引用确定的内存区域的表达式值。每当在需要右值的位置使用左值时,编译器都会执行左值到右值的转换,然后继续进行计算。

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&)。但是,如果我们从重载构造函数列表中删除 A::A(A&),则返回的临时对象仍将与右值引用构造函数 A::A(A& ;&)

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

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

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

An lvalue is a value bound to a definitive region of memory whereas an rvalue is an expression value whose existence is temporary and who does not necessarily refer to a definitive region of memory. Whenever an lvalue is used in a position in which an rvalue is expected, the compiler performs an lvalue-to-rvalue conversion and then proceeds with evaluation.

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

Whenever we construct a temporary (anonymous) class object or return a temporary class object from a function, although the object is temporary, it is addressable. However, the object still is a valid rvalue. This means that the object is a) an addressable rvalue or b) is being implicitly converted from an lvalue to an rvalue when the compiler expects an lvalue to be used.

For instance:

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
}

We also know that temporary objects returned (in the following case a) by functions are lvalues as this code segment:

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

yields the following output:

int conversion ctor

lvalue copy ctor

Indicating that the call to the function ret_a using actual argument A(5) calls the conversion constructor A::A(int) which constructs the function's formal argument a with the value 5.

When the function completes execution, it then constructs a temporary A object using a as its argument, which invokes A::A(A&). However, if we were to remove A::A(A&) from the list of overloaded constructors, the returned temporary object would still match the rvalue-reference constructor A::A(A&&).

This is what I'm not quite understanding: how can the object a match both an rvalue reference and an lvalue reference? It is clear that A::A(A&) is a better match than A::A(A&&) (and therefore a must be an lvalue). But, because an rvalue reference cannot be initialized to an lvalue, given that the formal argument a is an lvalue, it should not be able to match the call to A::A(A&&). If the compiler is making an lvalue-to-rvalue conversion it would be trivial. The fact that a conversion from 'A' to 'A&' is also trivial, both functions should have identical implicit conversion sequence ranks and therefore, the compiler should not be able to deduce the best-matching function when both A::A(A&) and A::A(A&&) are in the overloaded function candidate set.

Moreover, the question (which I previously asked) is:

How can a given object match both an rvalue reference and an lvalue reference?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

扭转时空 2024-10-28 07:58:49

对我来说:

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

产量:(

int conversion ctor
rvalue copy ctor

即右值,而不是左值)。这是您的编译器中的错误。不过,这是可以理解的,因为这种行为的规则仅在几个月前(2010 年 11 月)发生了变化。下面详细介绍这一点。

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

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

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

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

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

引用声明:

return a;

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

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

For me:

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

Yields:

int conversion ctor
rvalue copy ctor

(i.e. rvalue, not lvalue). This is a bug in your compiler. However it is understandable as the rules for this behavior changed only a few months ago (Nov. 2010). More on this below.

When the function completes execution,
it then constructs a temporary A
object using a as its argument, which
invokes A::A(A&).

Actually no. When the function ret_a completes execution, it then constructs a temporary A object using a as its argument, which invokes A:A(A&&). This is due to [class.copy]/p33]1:

When the criteria for elision of a
copy operation are met or would be met
save for the fact that the source
object is a function parameter, and
the object to be copied is designated
by an lvalue, overload resolution to
select the constructor for the copy is
first performed as if the object were
designated by an rvalue. If overload
resolution fails, or if the type of
the first parameter of the selected
constructor is not an rvalue reference
to the object’s type (possibly
cv-qualified), overload resolution is
performed again, considering the
object as an lvalue. [ Note: This
two-stage overload resolution must be
performed regardless of whether copy
elision will occur. It determines the
constructor to be called if elision is
not performed, and the selected
constructor must be accessible even if
the call is elided. — end note ]

However if you remove the A::A(A&&) constructor, then A::A(&) will be chosen for the return. Although in this case, then the construction of the argument a will fail because you can't construct it using an rvalue. However ignoring that for the moment, I believe your ultimate question is:

How can a given object match both an
rvalue reference and an lvalue
reference?

in referring to the statement:

return a;

And the answer is in the above quoted paragraph from the draft standard: First overload resolution is tried as if a is an rvalue. And if that fails, overload resolution is tried again using a as an lvalue. This two-stage process is tried only in the context wherein copy elision is permissible (such as a return statement).

The C++0x draft has just recently been changed to allow the two-stage overload resolution process when returning arguments that have been passed by value (as in your example). And that is the reason for the varying behavior from different compilers that we are seeing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文