右值结构的成员是右值还是左值?
返回结构的函数调用是右值表达式,但是它的成员呢?
这段代码与我的 g++ 编译器配合得很好,但是 gcc 给出了一个错误,指出“需要左值作为赋值的左操作数”:
struct A
{
int v;
};
struct A fun()
{
struct A tmp;
return tmp;
}
int main()
{
fun().v = 1;
}
gcc 将 fun().v
视为右值,我可以理解这一点。
但g++并不认为赋值表达式是错误的。这是否意味着 fun1().v 是 C++ 中的左值?
现在的问题是,我搜索了 C++98/03 标准,没有发现任何关于 fun().v
是左值还是右值的信息。
那么,它是什么?
A function call returning a structure is an rvalue expression, but what about its members?
This piece of code works well with my g++ compiler, but gcc gives a error saying "lvalue required as left operand of assignment":
struct A
{
int v;
};
struct A fun()
{
struct A tmp;
return tmp;
}
int main()
{
fun().v = 1;
}
gcc treats fun().v
as rvalue, and I can understand that.
But g++ doesn't think the assignment expression is wrong. Does that mean fun1().v is lvalue in C++?
Now the problem is, I searched the C++98/03 standard, finding nothing telling about whether fun().v
is lvalue or rvalue.
So, what is it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
右值表达式的成员是右值。
该标准在 5.3.5 [expr.ref] 中规定:
A member of an rvalue expression is an rvalue.
The standard states in 5.3.5 [expr.ref]:
现在是了解什么是xvalues 和 glvalues 的好时机。
右值可以有两种类型 - 右值和x值。根据新的C++17标准
因此,您的示例中类似
fun()
的计算结果为纯右值(即右值)。这也告诉我们 fun().v 不是纯右值,因为它不是普通的初始化。Xvalues 也是右值,定义如下
除了右值之外,另一个伞值类别是glvalue,它有两种类型xvalues 和传统的lvalues。
至此,我们已经定义了基本价值类别。这可以像这样可视化
类别 glvalue 可以广泛地被认为意味着在移动语义成为事物之前 lvalues 应该意味着什么 -可以位于表达式左侧的事物。 glvalue 表示广义左值。
如果我们查看xvalue的定义,那么如果某个东西接近其生命周期的终点,那么它就是一个xvalue。在您的示例中,
fun().v
即将结束其生命周期。所以它的资源是可以移动的。而且由于它的资源可以移动,所以它不是左值,因此您的表达式适合剩下的唯一叶值类别 - xvalue。This is a good time to learn about what xvalues an glvalues are.
Rvalues can be of two types - prvalues and xvalues. According to the new C++17 standard
so something like
fun()
in your example evaluates to an prvalue (which is an rvalue). This also tells us thatfun().v
is not a prvalue, since it is not a vanilla initialization.Xvalues which are also rvalues are defined like so
In addition to rvalues, another umbrella value category is a glvalue which be of two types xvalues and the traditional lvalues.
We have at this point defined the essential value categories. This can be visualized like so
The category glvalue can broadly be thought to mean what lvalues were supposed to mean before move semantics became a thing - a thing that can be on the left hand side of an expression. glvalue means generalized lvalue.
If we look at the definition of an xvalue, then it says something is an xvalue if it is near the end of its lifetime. In your example,
fun().v
is near the end of its lifetime. So its resources can be moved. And since its resources can be moved it is not an lvalue, therefore your expression fits in the only leaf value category that remains - an xvalue.编辑:好的,我想我终于从标准中得到了一些东西:
请注意,
v
是int
类型,它有一个内置的赋值运算符:fun1()
应返回一个引用。函数的非引用/指针返回类型是右值。因此,
fun1().v
是右值。Edit: Ok, I guess I finally have something from the standard:
Note that
v
is of typeint
which has an built-in assignment operator:fun1()
should return a reference. A non-reference/pointer return type of a function is a r-value.Thusly,
fun1().v
is a rvalue.我注意到 gcc 对于在赋值表达式中使用右值作为左值几乎没有什么顾虑。例如,编译得很好:
为什么这是合法的,而这是不合法的(即它不能编译),尽管这确实让我困惑:
恕我直言,标准委员会对左值、右值以及它们可以在哪里进行一些解释被使用。这是我对这个关于右值的问题如此感兴趣的原因之一< /a>.
I've noticed that gcc tends to have very few compunctions about using rvalues as lvalues in assignment expressions. This, for example, compiles just fine:
Why that's legal and this isn't (i.e. it doesn't compile) though really confuses me:
IMHO, the standard committee has some explaining to do with regards to lvalues, rvalues and where they can be used. It's one of the reasons I'm so interested in this question about rvalues.
当您考虑到编译器将为您生成默认构造函数、默认复制构造函数和默认复制赋值运算符(以防您的结构/类不包含引用成员)时,这一点就变得显而易见了。然后,想想标准允许您在临时对象上调用成员方法,也就是说,您可以在非常量临时对象上调用非常量成员。
看这个例子:
如果你
这样写,即如果你让函数 foo() 返回一个 const 临时变量,则会发生编译错误。
为了使示例完整,以下是如何调用 const temporarie 的成员:
您可以将临时变量的生存期定义在当前表达式内。这就是为什么你也可以修改成员变量;如果你不能,那么调用非常量成员方法的可能性就会变得荒谬。
编辑:这里是所需的引用:
12.2 临时对象:
然后(或者更好,之前)
3.10 左值和右值:
和一个示例使用: http://en.wikibooks.org/wiki/更多_C%2B%2B_Idioms/Named_Parameter
It becomes obvious when you consider that the compiler will generate a default constructor, a default copy constructor, and a default copy assignment operator for you, in case your struct/class does not contain reference members. Then, think of that the standard allows you to call member methods on temporaries, that is, you can call non-const members on non-const temporaries.
See this example:
If you write
i.e. if you let function foo() return a const temporary, then a compile error occurs.
To make the example complete, here is how to call a member of a const temporarie:
You could define the lifetime of a temporary to be within the current expression. And then that's why you can also modify member variables; if you couldn't, than the possibility of being able to call non-const member methods would be led ad absurdum.
edit: And here are the required citations:
12.2 Temporary objects:
and then (or better, before)
3.10 Lvalues and rvalues:
And an example use: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter
你的代码没有场景。返回的结构在堆栈上分配,因此赋值结果将立即丢失。
您的函数应该通过以下方式分配 A 的新实例:
在这种情况下更好的签名
或返回现有实例,例如:
You code has no scene. Returned structure is allocated on stack, so assignment result is immediately will be lost.
Your function should eiter allocate new instance of A by:
In this case better signature
Or return existing instance, for example: