prvalue 表达式的“结果对象”实际上是什么?

What actually a "result object" of a prvalue expression is?

提问人:mada 提问时间:10/11/2023 更新时间:10/12/2023 访问量:178

问:

该标准在 [basic.lval]/5 中规定如下:

[..]prvalue 的结果对象是由 prvalue [..]

它与 [basic.lval]/(1.2) 中所述的 prvalue 定义同步:

prvalue 是一个表达式,其计算结果初始化对象 [..]

所以据我了解,对 prvalue 表达式的计算初始化了一个名为“结果对象”的对象,而这个“结果对象”就是由这个 prvalue 初始化的对象。例如:

struct S {};

int main(void){
    S s = S();
}

这是一个 prvalue 表达式,我认为这是它的结果对象(可能不正确,这就是我所要求的),因为根据第一个引号,是由 prvalue 初始化的对象。现在,如果实际上是结果对象,那么结果对象在哪里:S()ssS()s

struct X {};

int main(void){
     X();
}

这里 PRvalue 没有 result 对象,因为它没有被任何对象初始化;右?X()


当我在 Clang AST 中看到以下代码时,我陷入了这种困惑: (很抱歉试图打开一个不相关的问题,但我会总结 AFAIC)

struct Y {};

int main() { 
    Y y = Y(); 
}

以下是从上述代码生成的 AST:(...省略...

....
...
`-FunctionDecl <line:5:1, line:7:1> line:5:5 main 'int ()'
  `-CompoundStmt <col:12, line:7:1>
    `-DeclStmt <line:6:5, col:14>
      `-VarDecl <col:5, col:13> col:7 y 'Y':'Y' cinit
        `-CXXTemporaryObjectExpr <col:11, col:13> 'Y':'Y' 'void () noexcept' zeroing

最后一行清楚地表明创建了一个未实现的临时 get。当我试图理解为什么正确或错误地创建这个临时对象时,我在标准中检查了以下文本:([class.temporary]/3)

当类类型 X 的对象传递给函数或从函数返回时,如果 X 至少有一个合格的复制或移动构造函数 ([special]),则每个此类构造函数都是微不足道的,并且 X 的析构函数是微不足道的或已删除的,则允许实现创建一个临时对象来保存函数参数或结果对象

[..]

我不确定我是否发现了正确的段落。但是如果是这样,上面的段落说创建的临时对象包含“结果对象”,我看到这里的结果对象是;右?我在这里感到困惑:结果对象如何同时尚未初始化?这里的结果对象是否是从构造函数调用生成的对象?还是别的什么?yyy

如果我的问题有任何冲突,或者这些问题是基于误解,我深表歉意。

C++ 语言律师 临时对象 结果对象

评论

3赞 Aconcagua 10/11/2023
X()实际上确实创建了一个对象——一个临时的对象,在创建后立即再次销毁。您会注意到,如果您实现了一个自定义类,该类在构造函数和析构函数中提供了一些输出。这个对象,虽然没有赋值到任何地方,但它是结果对象......
0赞 mada 10/11/2023
@Aconcagua - 结果对象的定义是由 prvalue 初始化的对象 - 那么由 prvalue 初始化的对象是什么?这个对象是临时对象吗?X()
0赞 Aconcagua 10/11/2023
正是那个(“一个临时的[...]”)。
1赞 Aconcagua 10/11/2023
@mada 老实说,在这种特定情况下,我不完全确定;首先来看更简单的情况:如果您之前已经创建了对象,那么会创建一个临时对象,该对象将传递给复制(或移动)赋值运算符 (for )。在本例中,此临时对象是结果对象。 – 如果我没记错的话,这个表达式会导致直接初始化,而没有任何中间临时(没有调用复制构造函数! – 如果确实如此,那么也是结果对象,但不会让我的手着火......YY y; y = Y();yY y = Y();yy
1赞 j6t 10/12/2023
如果你的意思是 ,那么你是对的,没有发生临时的物化。我应该更具体一点:当你像另一个例子一样编写时(即,这是整个语句),然后会发生临时物化转换,因为这是一个丢弃的值表达式Y()Y y = Y();X();

答:

0赞 user17732522 10/12/2023 #1

and I think that s is its result object

Yes, correct. In particular there is no temporary object that could be the result object instead (since C++17).

here X() prvalue has no result object because it isn't initialized by any objects; Right?

The expression in the expression statement is a discarded-value expression per [stmt.expr]/1.X()X();

After potential conversions which are not relevant here, a prvalue expression that is a discarded-value expression has the temporary materialization conversion applied per [expr.context]/2.

The effect of temporary materialization conversion is to introduce a temporary object that is initialized from the prvalue per [conv.rval].

This temporary object is therefore the result object of the prvalue.

I fell into this confusion when I saw the following code in Clang AST

CXXTemporaryObjectExpr probably (I am guessing here) only indicates an expression of the form where is a non-reference type and is either an empty expression list or a list of two or more expressions and isn't intended to state anything about applied temporary materialization conversions.T(E)TE

Before C++17, this syntax generally caused the creation of a temporary object (before optional copy elision), which is why the name makes sense. Since C++17 there are many cases where this expression syntax will not cause the creation of a temporary object as you demonstrate. They probably simply didn't change the Clang-internal naming.

Your quote from the standard is not relevant here since there is no function that takes or returns a .S


Note that the above applies to C++17 and later only.

评论

0赞 mada 10/12/2023
Thanks for your answer. And I am glad to see you agree with me that the last wording is somehow irrelevant as I thought and clarified in the question. But clang manual says that CXXTemporaryObjectExpr Represents a C++ functional cast expression that builds a temporary object - Doesn't that mean there's a temporary created as a result of this cast expression?
0赞 user17732522 10/12/2023
@mada I guess the documentation is just imprecise or wasn't correctly updated for C++17.
0赞 user17732522 10/12/2023
@mada The example they give in the documentation also doesn't necessarily imply construction of a temporary depending on the use at the call site. (Although in that case [class.temporary]/3 does apply and depending on the ABI there may be a temporary introduced regardless of the use at the call site.)
0赞 mada 10/12/2023
Accordingly, (since c++17) you see that there are no temporaries necessarily created in case ?Y y = Y()
0赞 user17732522 10/12/2023
@mada It is guaranteed that there is no temporary created in that statement (since C++17).