临时初始化和参考初始化
我想了解如何引用初始化。例如,我们看一个典型的例子。
double val = 4.55;
const int &ref = val;
我可以想到上面代码片段中发生的事情的两种可能性。
可能性 1
通常的解释如下:
这里创建了一个 int
类型、值为 4
的临时(纯右值),然后引用 < code>ref 绑定到这个临时(prvalue)int
对象而不是绑定到变量val 直接。发生这种情况是因为右侧变量
val
的类型是 double
,而左侧我们引用了 int
。但是为了将引用绑定到变量,类型应该匹配。此外,临时纯右值的生命周期也得到了延长。
可能性 2
我认为还有另一种可能发生的可能性如下:
这里一个类型为 int
且值为 4
的临时(纯右值)是创建的。但由于 const int &ref
需要一个 glvalue 并且当前我们有一个 纯右值,因此临时物化开始,因此纯右值被转换到xvalue。然后引用ref
绑定到这个物化xvalue(因为xvalue也是一个glvalue)而不是绑定到变量val< /code> 直接。发生这种情况是因为右侧变量
val
的类型是 double
,而左侧我们引用了 int
。但是为了将引用绑定到变量,类型应该匹配。此外,物化临时 xvalue 的生命周期也延长了。
我的问题是:
- 根据 C++11 标准,上述哪种解释是正确的。我愿意接受上述解释都不正确,在这种情况下,正确的解释是什么。
- 根据C++17标准,以上解释哪一个是正确的。我愿意接受上述解释都不正确,在这种情况下,正确的解释是什么。
- 我也很困惑上述两种可能性的第一步中的纯右值是否实际上是一个临时对象?或者 xvalue 是实际对象。我的意思是我们是否有 2 个临时对象,例如第一个由于“纯右值转换”而导致的,第二个由于“纯右值到 xvalue”转换(临时物质化)而导致。或者我们是否只有一个临时值,这是由于“prvalue to xvalue”临时物化所致。
PS:我并不是在寻找解决这个问题的方法。例如,我知道我可以简单地写: const double &ref = val;
。我的目标是根据 C++11 和 C++17 标准了解正在发生的情况。
I am trying to understand how reference initialization. For example, let's look at a typical example.
double val = 4.55;
const int &ref = val;
I can think of 2 possibilities of what is happening in the above snippet.
Possibility 1
The usual explanation is given as follows:
Here a temporary(prvalue) of type int
with value 4
is created and then the reference ref
is bound to this temporary(prvalue) int
object instead of binding to the variable val
directly. This happens because the type of the variable val
on the right hand side is double
while on the left hand side we have a reference to int
. But for binding a reference to a variable the types should match. Moreover, the lifetime of the temporary prvalue is extended.
Possibility 2
I think there is another possibility that could happen which is as follows:
Here a temporary(prvalue) of type int
with value 4
is created. But since const int &ref
expects a glvalue and currently we've a prvalue, the temporary materialization kicks in and so the prvalue is converted to an xvalue. Then the reference ref
is bound to this materialized xvalue(since xvalue is also a glvalue) instead of binding to the variable val
directly. This happens because the type of the variable val
on the right hand side is double
while on the left hand side we have a reference to int
. But for binding a reference to a variable the types should match. Moreover, the lifetime of the materialized temporary xvalue is extended.
My questions are:
- Which of the above explanation is correct according to the C++11 standard. I am open to accept that none of the explanation above is correct in which case what is the correct explanation.
- Which of the above explanation is correct according to the C++17 standard. I am open to accept that none of the explanation above is correct in which case what is the correct explanation.
- I am also confused to whether a prvalue in the first step of both of the possibilities above, is actually a temporary object? Or the xvalue is the actual object. I mean do we have 2 temporary objects, like the first one due to "conversion to prvalue" and second one due to the "prvalue to xvalue" conversion(temporary materiliazation). Or do we only have one temporary which is due to the "prvalue to xvalue" temporary materialization.
PS: I am not looking for a way to solve this. For example, i know that i can simply write:const double &ref = val;
. My aim is to understand what is happening according to C++11 and C++17 standards.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
const int &ref = val;
中的val
是左值,而不是纯右值。即使它被转换为纯右值,这并不意味着在 C++11 或 C++17 中创建临时值。在 C++17 中,情况并非如此,因为只有纯右值到 xvalue 的转换(以及一些与此处不相关的特殊情况)会创建临时值,而在 C++11 中 [conv.lval]/2 表示仅针对类类型左值到右值转换创建临时值。引用的初始化在 [dcl.init.ref] 中进行了解释。
在 C++11 中,所有以前的情况都会失败,因此根据 [dcl.init.ref]/5.2.2 目标类型的临时对象是通过初始化表达式的复制初始化创建和初始化的,并且引用绑定到该临时对象。在此复制初始化中,左值
val
转换为double
类型的纯右值,然后转换为int
类型的纯右值,这两个步骤都没有创建额外的临时对象。在 C++17 中,所有情况都会失败,直到 [dcl .init.ref]/5.2.2,它指出初始化表达式首先隐式转换为目标类型的纯右值,这并不意味着创建临时对象,然后创建临时对象应用物化转换(纯右值到x值转换)并将引用绑定到结果,即引用临时值的x值。
最后总是有一个临时对象,即根据 [dcl.init.ref] 中的规则创建的临时对象。
val
inconst int &ref = val;
is a lvalue, not a prvalue. Even if it is converted to a prvalue, this doesn't mean creation of a temporary in either C++11 or C++17. In C++17 this isn't the case since only prvalue-to-xvalue conversion (and some special cases not relevant here) creates a temporary, while in C++11 [conv.lval]/2 says that only for class types lvalue-to-rvalue conversion creates a temporary.The initialization of a reference is explained in [dcl.init.ref].
In C++11, all previous cases fall through and so according to [dcl.init.ref]/5.2.2 a temporary of the destination type is created and initialized by copy-initialization from the initializer expression and the reference is bound to that temporary. In this copy-initialization the lvalue
val
is converted to a prvalue of typedouble
and then to a prvalue of typeint
, neither of these steps creating additional temporaries.In C++17, all cases fall through until [dcl.init.ref]/5.2.2, which states that the initializer expression is first implicitly converted to a prvalue of the destination type, which does not imply creation of a temporary, and then the temporary materialization conversion (prvalue-to-xvalue conversion) is applied and the reference bound to the result, i.e. the xvalue referring to the temporary.
In the end there is always exactly one temporary, the one created according to the rule in [dcl.init.ref].
让我们分别看看每种情况(C++11 与 C++17)。
C++11
来自 decl.init.ref 5.2。 2:
需要注意的一件更重要的事情是来自 basic.lval#4:
当应用于您的示例时,这意味着将创建一个
int
类型的临时值,并从初始化表达式进行初始化val
使用非引用复制初始化的规则。如果/当用作表达式时,如此创建的临时int
的值类别为prvalue。接下来,引用
ref
被绑定到创建的临时int
,其值为4
。因此,C++17
来自 decl.init.ref 5.2.2.2:
当应用于您的示例时,这意味着初始化表达式
val
会隐式转换为const int
类型的纯右值。现在我们现在有一个纯右值const int。但在应用临时物化之前, expr 6 会启动,其中显示:这意味着在发生临时实现之前,纯右值 const int 会调整为纯右值 int 。
最后,应用临时物化,并将
ref
绑定到生成的xvalue
。Lets look at each of the cases(C++11 vs C++17) separately.
C++11
From decl.init.ref 5.2.2:
One more important thing to note is that from basic.lval#4:
When applied to your example, this means that a temporary of type
int
is created and is initialized from the initializer expressionval
using the rules for a non-reference copy-initialization. The temporaryint
so created has the value category of prvalue if/when used as an expression.Next, the reference
ref
is then bound to the temporaryint
created which has value4
. Thus,C++17
From decl.init.ref 5.2.2.2:
When applied to your example, this means that the initializer expression
val
is implicitly converted to a prvalue of typeconst int
. Now we currently have a prvalueconst int
. But before temporary materialization is applied, expr 6 kicks in which says:This means before temporary materialization could happen, the prvalue
const int
is adjusted to prvalueint
.Finally, temporary materialization is applied and and
ref
is bound to the resultingxvalue
.以下是我对 C++17 的看法:
我们将一个
const int
引用绑定到由val
表示的double
类型的左值表达式。根据引用声明;我们将表达式
val
转换为类型int
。注意这个隐式类型!转换符合草案。下一个,
为了应用临时物化(也称为纯右值 -> xvalue 转换),我们必须具有完全匹配的类型,因此表达式
另外转换为被认为是const int
const int
而不进行任何转换(来源)。值类别保持不变(纯右值)。最后,我们有一个
T
类型的引用绑定到T
类型的纯右值,这会导致临时物化(source) 和val
转换为 xvalue。创建的临时对象的类型为 const int,值为4
。编辑:当然,也是为了回答你的问题。因此,从技术上来说,对于 C++17 来说,这两种可能性从一开始就不正确。严格来说,纯右值和x值都不是临时对象。相反,它们是表达式的值类别,并且表达式可能(也可能不)表示(临时)对象。因此从技术上讲,xvalue 表示临时对象,而纯右值则不然。您仍然可以说 xvalues 是临时对象,只要您知道自己在说什么,我就完全同意。
Here's my take for C++17:
We're binding a
const int
reference to the lvalue expression of typedouble
denoted byval
. According to the declaration of references;we convert the expression
val
to typeint
. Note that this implicit type! conversion fits with the draft.Next,
in order to apply the temporary materialization (also known as prvalue -> xvalue conversion), we must have fully matched types so the expression
is aditionally converted tois considered to beconst int
const int
without any conversions (source). The value category remains the same (prvalue). And finally,we have a reference binding of type
T
to a prvalue of typeT
which induces temporary materialization (source) andval
gets converted to xvalue. The created temporary object is of typeconst int
and value of4
.EDIT: And to answer your questions, of course. So neither of the given possibilities aren't technically correct for C++17 right at the start. Strictly speaking, neither prvalues nor xvalues are temporary objects. Rather, they are value category of expressions and expression might (or might not) denote a (temporary) object. So technically, xvalues denote temporary objects while prvalues don't. You could still say that xvalues are temporary objects, that's completely fine by me as long as you know what you're talking about.