关于如何识别Rvalue或Lvalue引用和if-it-has-a-name规则 [英] On how to recognize Rvalue or Lvalue reference and if-it-has-a-name rule

查看:156
本文介绍了关于如何识别Rvalue或Lvalue引用和if-it-has-a-name规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读Thomas Becker的关于右值引用及其使用的文章。在那里他定义了他所说的 if-it-has-a-name 规则:


声明为右值引用可以是lvalues或
rvalues。区分标准是:如果它有一个名称,那么它是
一个左值。否则,它是一个右值。


这听起来很合理。它还清楚地标识了右值引用的重要性。



我的问题是:


  1. 您同意这项规定吗?如果没有,您可以举一个违反此规则的示例吗?

  2. 如果没有违反此规则。我们可以使用这个规则来定义表达式的简洁性/简单性吗?


解决方案

最常见的经验法则用于解释左值和右值之间的区别。



C ++的情况比这更复杂,只不过是一个经验法则。我将尝试恢复几个概念,并试图澄清为什么这个问题在C ++世界是如此复杂。首先让我们回顾一下发生了什么事情。



开始时有C



lvalue和rvalue通常是指在编程语言的世界中最初的意思?



在像C或Pascal这样更简单的语言中,可以放置在赋值运算符的 之下。



像Pascal这样的语言,其中赋值不是一个表达式,而只是一个语句,区别很清楚,它用语法术语定义。左值是变量的名称或数组的下标。



这是因为只有这两个东西可以位于一个赋值的左边:

  i:= 42; (* ok *)
a [i]:= 42; (* ok *)
42:= 42; (*无意义*)

在C中,同样的差异适用,在某种意义上,你可以看一行代码,并告诉一个表达式是否会产生一个左值或右值。

  i = 42; // ok,变量
* p = 42; // ok,指针解引用
a [i] = 42; // ok,下标(无论如何是一个指针引用)
s-> var = 42; //确定,结构成员访问

那么C ++中有什么改变?



小语言成长



在C ++中,事情变得更加复杂,差异不再是语法,而是涉及到类型检查过程,原因有两个:




  • 一切都可以停留在任务的左边,只要它的类型具有适当的重载 operator =

  • 参考



所以这意味着在C ++中,你不能说如果一个表达式只会通过查看它的语法结构产生一个左值。例如:

  f()= g(); 

是在C语言中没有意义的语句,但在C ++中可以是完全合法的语句, f()返回引用。这是 v [i] = j 的表达式为 std :: vector 工作:运算符[] 返回对该元素的引用,以便您可以分配给它。



因此,区分lvalue和rval不再?该区别仍然与基本类型的课程相关,但也决定了可以绑定到非const引用。



这是因为您不想拥有如下的法律代码:

  int& x = 42; 
x = 0; //我们改变了自然数的意义吗?

所以语言详细说明了什么是左值,什么是左值,然后说只有左值可以绑定到非const引用。所以上面的代码是不合法的,因为一个整数字面值不是一个左值,所以一个非const引用不能绑定到它。



注意 const 引用是不同的,因为它们可以绑定到文字和临时(和局部引用甚至延长这些临时的生命周期):

  int const& x = 42; // It's ok 

到现在为止,我们只碰到了C ++ 98。规则已经比如果它有一个名称是一个左值更复杂,因为你必须考虑参考。所以返回非const引用的表达式仍然被认为是一个左值。



此外,这里提到的其他经验法则在所有情况下都不起作用。例如如果你可以拿它的地址,它是一个左值。如果通过取地址你的意思是应用运算符& ,那么它可能工作,但不要欺骗自己,以为你不能来临时的地址:例如,临时成员函数中的 this 指针将指向它。



C ++中发生了什么变化11



C ++ 11通过添加右值引用的概念,是,可以绑定到右值的引用,即使非const。它只能应用于右值的事实使得它既安全又有用。我不认为它需要解释为什么右值引用有用,所以继续。



这里的要点是,现在我们有更多的案例要考虑。那么什么是右值呢?标准实际上区分不同种类的右值,以便能够在存在右值引用的情况下正确地陈述右值引用的行为和重载分辨率以及模板参数推导。所以我们有 xvalue prvalue 这样的术语,这使事情更复杂。



我们的经验法则是什么?



所以有一个名字的东西是一个左值可以是真的,肯定不是真的每个左值有一个名字。返回非常量值引用的函数是一个左值。按值返回值的函数创建一个临时值,它是一个右值,所以返回一个右值引用的函数。



临时值是rvalues。这是真的,但也可以通过简单的转型类型(如 std :: move )将非临时变为右值。

所以我认为所有这些规则是有用的,如果我们记住它们是什么:经验法则。
他们总是有一些不适用的角落情况,因为为了精确指定一个右值是什么,什么不是,我们不能避免使用标准中使用的准确的术语和规则。这就是为什么他们写的!


I was reading Thomas Becker's article on rvalue reference and their use. In there he defines what he calls if-it-has-a-name rule:

Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.

This sounds very reasonable to me. It also clearly identifies the rvalueness of an rvalue reference.

My questions are:

  1. Do you agree with this rule? If not, can you give an example where this rule can be violated?
  2. If there are no violations of this rule. Can we use this rule to define rvalueness/lvaluness of an expression?

解决方案

This is one of the most common "rules of thumb" used to explain what is the difference between lvalues and rvalues.

The situation in C++ is much more complex than that so this can't be nothing but a rule of thumb. I'll try to resume a couple of concepts and try to make it clear why this issue is so complex in the C++ world. First let's recap a bit what happened once upon a time

At the beginning there was C

First, what "lvalue" and "rvalue" used to mean originally, in the world of programming languages in general?

In a simpler language like C or Pascal, the terms used to refer to what could be placed at the Left or at the Right of an assignment operator.

In a language like Pascal where the assignment is not an expression but only a statement, the difference is pretty clear and it's defined in grammatical terms. An lvalue is a name of a variable, or a subscript of an array.

That's because only these two things could stand at the left of an assignment:

i := 42; (* ok *)
a[i] := 42; (* ok *)
42 := 42; (* no sense *)

In C, the same difference applies, and it is still pretty much grammatical in the sense that you could look at a line of code and tell if an expression would produce an lvalue or an rvalue.

i = 42; // ok, a variable
*p = 42; // ok, a pointer dereference
a[i] = 42; // ok, a subscript (which is a pointer dereference anyway)
s->var = 42; // ok, a struct member access

So what changed in C++?

Little languages grow up

In C++ things become much more complex and the difference is not grammatical anymore but involves the type checking process, for two reasons:

  • Everything could stay at the left of an assignment, as long as its type has a suitable overload of operator=
  • References

So this means that in C++ you can't say if an expression will produce an lvalue only by looking at its grammatical structure. For example:

f() = g();

is a statement that would have no sense in C but can be perfectly legal in C++ if, for example, f() returns a reference. That's how expressions like v[i] = j work for std::vector: the operator[] returns a reference to the element so you can assign to it.

So what's the point of having a distinction between lvalues and rvalues anymore? The distinction is still relevant for basic types of course, but also to decide what can be bound to a non-const reference.

That's because you don't want to have legal code like:

int &x = 42;
x = 0; // Have we changed the meaning of a natural number??

So the language specifies carefully what is an lvalue and what isn't, and then says that only lvalues can be bound to non-const references. So the above code is not legal because an integer literal is not an lvalue so a non-const reference cannot be bound to it.

Note that const references are different, since they can bind to literals and temporaries (and local references even extend the lifetime of those temporaries):

int const&x = 42; // It's ok

And until now we've only touched what already used to happen in C++98. The rules were already more complex than "if it has a name it's an lvalue", since you have to consider the references. So an expression returning a non-const reference is still considered an lvalue.

Also, other rules of thumb mentioned here already don't work in all cases. For example "if you can take it's address, it's an lvalue". If by "taking the address" you mean "applying operator&", then it might work, but don't trick yourself into thinking that you can't ever come to have the address of a temporary: The this pointer inside a temporary's member function, for example, will point to it.

What changed in C++11

C++11 puts more complexity into the bin by adding the concept of an rvalue reference, that is, a reference that can be bound to an rvalue even if non-const. The fact that it can only be applied to an rvalue make it both safe and useful. I don't think its needed to explain why rvalue reference are useful, so move on.

The point here is that now we have a lot more of cases to consider. So what is an rvalue now? The Standard actually distinguish between different kinds of rvalues to be able to correctly state the behavior of rvalue references and overload resolution and template argument deduction in the presence of rvalue references. So we have terms like xvalue, prvalue and things like that, which make things more complex.

What about our rules of thumb?

So "everything that has a name is an lvalue" can still be true, but for sure it isn't true that every lvalue has a name. A function returning a non-const lvalue reference is an lvalue. A function returning something by value creates a temporary and it is an rvalue, so is a function returning an rvalue reference.

What about "temporaries are rvalues". It's true, but also non-temporaries can be made into rvalues by simply casting the type (as does std::move).

So I think that all these rules are useful if we keep in mind what they are: rules of thumb. They'll always have some corner case where they don't apply, because to exactly specify what an rvalue is and what isn't, we can't avoid using the exact terms and rules used in the standard. That's why they were written for!

这篇关于关于如何识别Rvalue或Lvalue引用和if-it-has-a-name规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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