orm*_*orm 41 c++ implicit-conversion lvalue-to-rvalue c++11
我看到整个C++标准中许多地方使用的术语"左值到右值转换".据我所知,这种转换通常是隐含的.
标准中的一个意外(对我来说)特征是他们决定将左值作为转换处理.如果他们说glvalue总是可以接受而不是prvalue怎么办?这句话实际上会有不同的含义吗?例如,我们读到lvalues和xvalues是glvalues的例子.我们没有读到lvalues和xvalues可以转换为glvalues.意义上有区别吗?
在我第一次遇到这个术语之前,我曾经或多或少地对lvalues和rvalues进行了如下建模:"lvalues 总是能够充当rvalues,但另外还可以出现在左侧=
和右侧&
".
对我来说,这是一个直观的行为,如果我有一个变量名,那么我可以将该名称放在我想放置文字的地方.此模型似乎与标准中使用的左值到右值隐式转换术语一致,只要保证发生此隐式转换即可.
但是,因为他们使用这个术语,我开始想知道在某些情况下是否可能无法进行隐式左值到右值转换.也就是说,也许我的心理模型在这里是错误的.以下是该标准的相关部分:(感谢评论者).
每当glvalue出现在期望prvalue的上下文中时,glvalue就会转换为prvalue; 见4.1,4.2和4.3.[注意:尝试将rvalue引用绑定到左值不是这样的上下文; 见8.5.3 .-结束说明]
我理解他们在说明中描述的内容如下:
int x = 1;
int && y = x; //in this declaration context, x won't bind to y.
// but the literal 1 would have bound, so this is one context where the implicit
// lvalue to rvalue conversion did not happen.
// The expression on right is an lvalue. if it had been a prvalue, it would have bound.
// Therefore, the lvalue to prvalue conversion did not happen (which is good).
Run Code Online (Sandbox Code Playgroud)
所以,我的问题是(是):
1)有人可以澄清这种转换可能隐含发生的背景吗?具体来说,除了绑定到右值引用的上下文之外,还有其他任何其中lvalue-to-rvalue转换无法隐式发生的情况吗?
2)此外,该[Note:...]
条款中的括号似乎使我们可以从之前的句子中找出它.标准的哪一部分是什么?
3)这是否意味着rvalue-reference绑定不是我们期望prvalue表达式的上下文(在右边)?
4)像其他转换一样,glvalue-to-prvalue转换是否涉及在运行时工作,这将允许我观察它?
我的目的不是要问是否允许进行这样的转换.我正在努力学习使用标准作为起点向自己解释此代码的行为.
一个好的答案将通过我上面的引用并解释(基于解析文本)其中的注释是否也隐含在其文本中.然后它可能会添加任何其他引号,让我知道这种转换可能无法隐式发生的其他上下文,或者解释不再有这样的上下文.也许对glvalue到prvalue的原因的一般讨论被认为是转换.
dyp*_*dyp 31
我认为左值到右值的转换不仅仅是使用需要右值的左值.它可以创建类的副本,并始终生成值,而不是对象.
我正在使用n3485代表"C++ 11"而n1256代表"C99".
最简洁的描述在C99/3.14中:
宾语
执行环境中的数据存储区域,其内容可以表示值
在C++ 11/[intro.object]/1中也有一点
有些对象是多态的 ; 该实现生成与每个这样的对象相关联的信息,使得可以在程序执行期间确定该对象的类型.对于其他对象,其中发现的值的解释由用于访问它们的表达式的类型确定.
所以一个对象包含一个值(可以包含).
尽管它的名称,值类别分类表达式,而不是值.左值表达式甚至不能被视为值.
完整的分类/分类可以在[basic.lval]中找到; 这是StackOverflow的讨论.
以下是有关对象的部分:
- 的左值([...])表示一函数或一个对象.[...]
- 一个x值(一个"到期"值)也指物体[...]
- 甲glvalue("广义"左值)是左值或x值.
- 的右值([...])是一个x值,临时对象或其子对象,或不与一个对象相关联的值.
- prvalue("纯"rvalue)是一个不是xvalue的rvalue.[...]
请注意短语"与对象无关的值".另请注意,当xvalue-expressions引用对象时,true 值必须始终作为prvalue-expressions出现.
如脚注53所示,现在应将其称为"glvalue-to-prvalue conversion".首先,这是引用:
1非函数非数组类型的glvalue
T
可以转换为prvalue.如果T
是不完整类型,则需要进行此转换的程序格式不正确.如果glvalue引用的对象不是类型的对象,T
并且不是派生类型T
的对象,或者如果对象未初始化,则需要此转换的程序具有未定义的行为.如果T
是非类类型,则prvalue的类型是cv-nonqualified versionT
.否则,prvalue的类型是T
.
第一段指定了要求和转换的结果类型.它还没有关注转换的影响(除了未定义的行为).
2当在未评估的操作数或其子表达式中发生左值到右值转换时,不会访问引用对象中包含的值.否则,如果glvalue具有类类型,则转换
T
将从glvalue 初始化一个临时类型,并且转换的结果是临时的prvalue.否则,如果glvalue具有(可能是cv-qualified)类型std::nullptr_t
,则prvalue结果是空指针常量.否则,glvalue指示的对象中包含的值是prvalue结果.
我认为你会看到最常用于非类型的左值到左值的转换.例如,
struct my_class { int m; };
my_class x{42};
my_class y{0};
x = y;
Run Code Online (Sandbox Code Playgroud)
表达x = y
并不能适用于左值到右值转换成y
(这将创建一个临时的my_class
,顺便说一句).其原因是,x = y
被解释为x.operator=(y)
,这需要y
每默认通过引用,而不是由值(供参考结合,参见下文;它不能结合一个rvalue,因为这将是一个临时对象从不同y
).但是,默认定义my_class::operator=
确实应用了左值到右值的转换x.m
.
因此,对我来说最重要的部分似乎是
否则,glvalue指示的对象中包含的值是prvalue结果.
通常,左值到右值的转换只会读取对象的值.它不仅仅是值(表达式)类别之间的无操作转换; 它甚至可以通过调用复制构造函数来创建临时文件.并且左值到右值的转换始终返回prvalue 值,而不是(临时)对象.
请注意,左值到右值的转换不是将左值转换为初始值的唯一转换:还有数组到指针的转换和函数到指针的转换.
大多数表达式都不会产生对象[[citation needed]].但是,id-expression可以是标识符,表示实体.对象是一个实体,因此有一些表达式可以产生对象:
int x;
x = 5;
Run Code Online (Sandbox Code Playgroud)
赋值表达式 的左侧x = 5
也需要是表达式.x
这是一个id-expression,因为它x
是一个标识符.此id-expression的结果是表示的对象x
.
表达式应用隐式转换:[expr]/9
每当glvalue表达式作为操作符的操作数出现,该操作符需要该操作数的prvalue时,将应用左值到右值,数组到指针或函数到指针的标准转换来将表达式转换为prvalue.
和/ 10关于通常的算术转换以及/ 3关于用户定义的转换.
我现在喜欢引用一个"期望该操作数的prvalue"的运算符,但除了强制转换之外找不到任何东西.例如,[expr.dynamic.cast]/2"如果T
是指针类型,v
[操作数]应该是指向完成类类型的指针的prvalue".
许多算术运算符所需的通常算术转换确实通过所使用的标准转换间接调用左值到右值的转换.所有标准转换,但从左值转换为右值的三个转换期望prvalues.
但是,简单赋值不会调用通常的算术转换.它在[expr.ass]/2中定义为:
在简单赋值(
=
)中,表达式的值替换左操作数引用的对象的值.
因此虽然它没有明确要求右侧的prvalue表达式,但它确实需要一个值.我不清楚这是否严格要求左值到左值的转换.有一个论点是访问未初始化变量的值应始终调用未定义的行为(也参见CWG 616),无论是通过将其值赋给对象还是将其值添加到另一个值.但是这种未定义的行为仅在左值到右值转换(AFAIK)时才需要,然后它应该是访问存储在对象中的值的唯一方法.
如果这个更概念化的视图是有效的,我们需要左值到右值的转换来访问对象内部的值,那么理解应用它(并且需要)的位置要容易得多.
与简单赋值一样,讨论是否需要左值到右值转换来初始化另一个对象:
int x = 42; // initializer is a non-string literal -> prvalue
int y = x; // initializer is an object / lvalue
Run Code Online (Sandbox Code Playgroud)
对于基本类型,[dcl.init]/17最后一个要点是:
否则,正在初始化的对象的初始值是初始化表达式的(可能已转换)值.如有必要,将使用标准转换将初始化表达式转换为目标类型的cv非限定版本; 不考虑用户定义的转换.如果无法进行转换,则初始化不正确.
但是,它还提到了初始化表达式的值.与simple-assignment-expression类似,我们可以将其作为lvalue-to-rvalue转换的间接调用.
如果我们看到左值到右值转换的方式来访问对象的值(加上类类型的操作数创建一个临时的),我们知道,它不是普遍适用结合的引用:引用是一个左值,它总是指一个物体.因此,如果我们将值绑定到引用,我们需要创建保存这些值的临时对象.如果引用的初始化表达式是prvalue(它是值或临时对象),情况确实如此:
int const& lr = 42; // create a temporary object, bind it to `r`
int&& rv = 42; // same
Run Code Online (Sandbox Code Playgroud)
禁止将prvalue绑定到左值引用,但是具有产生左值引用的转换函数的类类型的prvalue可以绑定到转换类型的左值引用.
[dcl.init.ref]中引用绑定的完整描述相当长而且非偏离主题.我认为它与此问题相关的本质是引用引用对象,因此没有glvalue到prvalue(对象到值)的转换.
关于左值:左值(“广义”左值)是一个可以是左值或x值的表达式。左值可以通过左值到右值、数组到指针或函数到指针隐式转换隐式转换为纯右值。
当在需要右值(例如数字)的上下文中使用左值参数(例如对对象的引用)时,将应用左值转换。
左值到右值转换
任何非函数、非数组类型的左值T
都可以隐式转换为相同类型的纯右值。如果T
是非类类型,则此转换还会删除 cv 限定符。除非在未求值的上下文中(在 sizeof、typeid、noexcept 或 decltype 的操作数中)遇到,否则此转换会使用原始泛左值作为构造函数参数有效地复制构造类型的临时对象T
,并且该临时对象作为纯右值返回。如果左值具有 类型std::nullptr_t
,则生成的纯右值是空指针常量nullptr
。
归档时间: |
|
查看次数: |
11732 次 |
最近记录: |