该标准在[basic.lval]/5中规定了以下内容:
[..]纯右值的结果对象是由纯右值[..]初始化的对象。
它与[basic.lval]/(1.2)中陈述的纯右值的定义同步:
纯右值是一个表达式,其计算结果是一个对象[..]
因此,据我所知,一个纯右值表达式的求值是一个名为“结果对象”的对象,而这个“结果对象”* 就是 * 这个纯右值初始化的对象。举例来说:
struct S {};
int main(void){
S s = S();
}
这里S()
是一个纯右值表达式,我认为s
是它的结果对象(可能不正确,这就是我所要求的),因为根据第一个引号,s
是由纯右值S()
初始化的对象。现在,如果s
实际上是结果对象,那么这里的结果对象在哪里:
struct X {};
int main(void){
X();
}
这里X()
纯右值没有结果对象,因为它没有被任何对象初始化;对不对?
当我在Clang AST中看到以下代码时,我陷入了这种困惑:(我很抱歉试图打开一个不相关的问题,但我会总结AFAIC)
struct Y {};
int main() {
Y y = Y();
}
下面是从上述代码生成的AST:(. omitted.)
....
...
`-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的析构函数是平凡的或被删除的,实现允许创建一个临时对象来保存函数参数或结果对象。
[..]
我不确定我是否找到了正确的段落。但如果是这样的话,上面的段落说创建的临时对象持有“结果对象”,我看到这里的结果对象是y
;对吧,我有点困惑结果对象是y
,而y
尚未初始化?这里的结果对象是从构造函数调用产生的对象吗?还是别的什么
如果我的问题中有任何冲突,或者如果这些问题是基于误解,我道歉。
1条答案
按热度按时间ldxq2e6h1#
我认为s是它的结果对象
是的没错特别地,没有临时对象可以代替结果对象(从C17开始)。
这里X()纯右值没有结果对象,因为它没有被任何对象初始化;对不对?
表达式语句
X();
中的表达式X()
是一个 * 带值表达式 * per [stmt.expr]/1。在这里不相关的潜在转换之后,一个纯右值表达式是一个双值表达式,它按照[expr.context]/2应用临时物化转换。
临时物化转换的效果是引入一个临时对象,该对象是根据conv.rval(https://timsong-cpp.github.io/cppwp/n4868/conv.rval)的纯右值初始化的。
因此,这个临时对象是纯右值的结果对象。
当我在Clang AST中看到以下代码时,
CXXTemporaryObjectExpr
可能(我在这里猜测)只表示T(E)
形式的表达式,其中T
是非引用类型,E
是一个空表达式列表或两个或多个表达式的列表,并且不打算说明任何关于应用的临时物化转换的内容。在C17之前,这种语法通常会导致临时对象的创建(在可选的复制省略之前),这就是为什么这个名字有意义。从C17开始,在很多情况下,这个表达式语法不会像你演示的那样导致临时对象的创建。他们可能只是没有改变叮当内部命名。
你引用的标准与此无关,因为没有接受或返回
S
的函数。请注意,以上内容仅适用于C17及更高版本。