临时初始化和参考初始化

发布于 2025-01-20 03:57:14 字数 1610 浏览 0 评论 0原文

我想了解如何引用初始化。例如,我们看一个典型的例子。

double val = 4.55;
const int &ref = val;

我可以想到上面代码片段中发生的事情的两种可能性。

可能性 1

通常的解释如下:

这里创建了一个 int 类型、值为 4 的临时(纯右值),然后引用 < code>ref 绑定到这个临时(prvalueint 对象而不是绑定到变量val 直接。发生这种情况是因为右侧变量 val 的类型是 double,而左侧我们引用了 int。但是为了将引用绑定到变量,类型应该匹配。此外,临时纯右值的生命周期也得到了延长。

可能性 2

我认为还有另一种可能发生的可能性如下:

这里一个类型为 int 且值为 4 的临时(纯右值)是创建的。但由于 const int &ref 需要一个 glvalue 并且当前我们有一个 纯右值,因此临时物化开始,因此纯右值被转换到xvalue。然后引用ref绑定到这个物化xvalue(因为xvalue也是一个glvalue)而不是绑定到变量val< /code> 直接。发生这种情况是因为右侧变量 val 的类型是 double,而左侧我们引用了 int。但是为了将引用绑定到变量,类型应该匹配。此外,物化临时 xvalue 的生命周期也延长了。

我的问题是:

  1. 根据 C++11 标准,上述哪种解释是正确的。我愿意接受上述解释都不正确,在这种情况下,正确的解释是什么。
  2. 根据C++17标准,以上解释哪一个是正确的。我愿意接受上述解释都不正确,在这种情况下,正确的解释是什么。
  3. 我也很困惑上述两种可能性的第一步中的纯右值是否实际上是一个临时对象?或者 xvalue 是实际对象。我的意思是我们是否有 2 个临时对象,例如第一个由于“纯右值转换”而导致的,第二个由于“纯右值到 xvalue”转换(临时物质化)而导致。或者我们是否只有一个临时值,这是由于“prvalue to xvalue”临时物化所致。

PS:我并不是在寻找解决这个问题的方法。例如,我知道我可以简单地写: const double &ref = val;。我的目标是根据 C++11C++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:

  1. 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.
  2. 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.
  3. 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 技术交流群。

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

发布评论

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

评论(3

無心 2025-01-27 03:57:14

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 in const 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 type double and then to a prvalue of type int, 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].

甜是你 2025-01-27 03:57:14

让我们分别看看每种情况(C++11 与 C++17)。

C++11

来自 decl.init.ref 5.2。 2:

否则,将使用非引用复制初始化 ([dcl.init]) 的规则从初始化表达式创建并初始化类型为“cv1 T1”的临时变量。然后,该引用将绑定到临时对象。

需要注意的一件更重要的事情是来自 basic.lval#4:

类纯右值可以具有 cv 限定的类型; 非类纯右值始终具有 cv 未限定的类型...

当应用于您的示例时,这意味着将创建一个 int 类型的临时值,并从初始化表达式进行初始化val 使用非引用复制初始化的规则。如果/当用作表达式时,如此创建的临时int 的值类别为prvalue

接下来,引用ref被绑定到创建的临时int,其值为4。因此,

double val = 4.55;
const int &ref = val; // ref refers to temporary with value 4

C++17

来自 decl.init.ref 5.2.2.2

否则,初始化表达式将隐式转换为“cv1 T1”类型的纯右值。应用临时物化转换并将引用绑定到结果。

当应用于您的示例时,这意味着初始化表达式 val 会隐式转换为 const int 类型的纯右值。现在我们现在有一个纯右值const int。但在应用临时物化之前, expr 6 会启动,其中显示:

如果纯右值最初具有类型“cv T”,其中 T 是 cv 未限定的非类、非数组类型,则表达式的类型在任何进一步分析之前调整为T

这意味着在发生临时实现之前,纯右值 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:

Otherwise, a temporary of type “ cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization ([dcl.init]). The reference is then bound to the temporary.

One more important thing to note is that from basic.lval#4:

Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types...

When applied to your example, this means that a temporary of type int is created and is initialized from the initializer expression val using the rules for a non-reference copy-initialization. The temporary int so created has the value category of prvalue if/when used as an expression.

Next, the reference ref is then bound to the temporary int created which has value 4. Thus,

double val = 4.55;
const int &ref = val; // ref refers to temporary with value 4

C++17

From decl.init.ref 5.2.2.2:

Otherwise, the initializer expression is implicitly converted to a prvalue of type “cv1 T1”. The temporary materialization conversion is applied and the reference is bound to the result.

When applied to your example, this means that the initializer expression val is implicitly converted to a prvalue of type const int. Now we currently have a prvalue const int. But before temporary materialization is applied, expr 6 kicks in which says:

If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

This means before temporary materialization could happen, the prvalue const int is adjusted to prvalue int.

Finally, temporary materialization is applied and and ref is bound to the resulting xvalue.

往昔成烟 2025-01-27 03:57:14

以下是我对 C++17 的看法:

double val = 4.55;
const int &ref = val;

我们将一个 const int 引用绑定到由 val 表示的 double 类型的左值表达式。根据引用声明

否则,初始化表达式将隐式转换为“T1”类型的纯右值。

我们将表达式 val 转换为类型 int。注意这个隐式类型!转换符合草案
下一个,

考虑纯右值的类型为“cv1 T1”,应用临时物化转换,...​​

为了应用临时物化(也称为纯右值 -> 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:

double val = 4.55;
const int &ref = val;

We're binding a const int reference to the lvalue expression of type double denoted by val. According to the declaration of references;

Otherwise, the initializer expression is implicitly converted to a prvalue of type “T1”.

we convert the expression val to type int. Note that this implicit type! conversion fits with the draft.
Next,

The temporary materialization conversion is applied, considering the type of the prvalue to be “cv1 T1”, ...

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 to const int is considered to be const int without any conversions (source). The value category remains the same (prvalue). And finally,

... and the reference is bound to the result.

we have a reference binding of type T to a prvalue of type T which induces temporary materialization (source) and val gets converted to xvalue. The created temporary object is of type const int and value of 4.

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.

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