C++ 在C+中,未赋值的非静态数据成员的左值类别的合理性是什么+;?
gcc和clang都接受下面的代码,我正试图找出原因 <代码> //C++ + STD= C++ 20 -墙-C测试 #包括 结构X{ int i; }; //这是语言规范明确要求的: 静态断言(标准::与相同); //这似乎更加武断: 静态断言(标准::与相同); 根据[dcl.type.decltype],第一行C++ 在C+中,未赋值的非静态数据成员的左值类别的合理性是什么+;?,c++,language-lawyer,c++20,decltype,C++,Language Lawyer,C++20,Decltype,gcc和clang都接受下面的代码,我正试图找出原因 //C++ + STD= C++ 20 -墙-C测试 #包括 结构X{ int i; }; //这是语言规范明确要求的: 静态断言(标准::与相同); //这似乎更加武断: 静态断言(标准::与相同); 根据[dcl.type.decltype],第一行static\u assert是有意义的: 否则,如果E是未加密的id表达式或未加密的类成员访问([expr.ref]),decltype(E)是由E命名的实体的类型。如果没有这样的实体,
static\u assert
是有意义的:
否则,如果E是未加密的id表达式或未加密的类成员访问([expr.ref]),decltype(E)是由E命名的实体的类型。如果没有这样的实体,或者如果E命名了一组重载函数,则程序的格式不正确
-
X::i
在未计算的上下文中是有效的,因此其decltype应该是X
中声明的i
类型
第二个静态断言让我难堪。[DCL,Type,DeCyType ]中只有一个子句,其中括号表达式产生一个LValk引用:这两个编译器必须考虑<代码>::i < /Cord>是LValk类的表达式。但是我在语言规范中找不到对此的任何支持
显然,如果i
是静态数据成员,那么X::i
将是一个左值。但对于非静态成员,我能找到的唯一提示是[expr.context]中的一些非规范性语言:
在某些上下文中,会出现未赋值的操作数([expr.prim.req]、[expr.typeid]、[expr.sizeof]、[expr.unary.noexcept]、[dcl.type.simple]、[temp.pre]、[temp.concept])。
不计算未计算的操作数。
[注意:在未赋值的操作数中,可以命名非静态类成员([expr.prim.id]),而对象或函数的命名本身并不要求提供定义([basic.def.odr])。
未赋值的操作数被视为完整表达式。
-尾注
]
-
这表明decltype((X::i))
是一个有效的类型,但没有说明任何关于值类别的内容。我看不出什么比int
(或int&
)更能证明int&
。我的意思是左值是glvalue,而glvalue是“一个表达式,它的求值决定了一个对象的标识。”像X::I
(它根本不能求值,更不用说确定对象的标识)这样的表达式怎么能被认为是glvalue呢
gcc和clang接受此代码是否正确,如果正确,语言规范的哪一部分支持此代码
备注:
讲故事人-Unslander Monica的回答更有意义,因为decltype((X::i+42))
是一个prvalue。你问题的根源似乎是decltype(X::i)
和decltype((X::i))
之间的差异。为什么(X::i)
会产生int&
?见:
否则,如果E是左值,decltype(E)是T&,其中T是E的类型
然而,这里的关键点是,它产生的T&
实际上并不重要:
如果表达式最初具有类型“reference to T”([dcl.ref]、[dcl.init.ref]),则在进行任何进一步分析之前,该类型将调整为T。表达式指定由引用表示的对象或函数,表达式是左值或x值,具体取决于表达式。[注意:在引用的生存期开始之前或结束之后,行为是未定义的(请参见[basic.life])。-结束注意]
那么,是什么“证明”了这一点?在执行decltype(X::i)
时,我们主要关注X::i
的类型,而不是它的值类别或它作为表达式处理时的属性。然而,如果我们在意的话,(X::i)
是存在的
像X::i
这样的表达式——根本无法计算,更不用说确定对象的标识了——怎么能被视为一个值
这并不完全正确。可以计算该表达式(在正确的上下文中进行适当的转换之后)
[class.mfct.non static]
当一个id表达式不属于类成员访问语法的一部分,也不用于形成指向成员([expr.unary.op])的指针时,如果名称查找将id表达式中的名称解析为某个类C的非静态非类型成员,如果id表达式可能被计算,或者C是X或X的基类,则id表达式将被转换为类成员访问表达式,使用(*this)作为表达式左侧的后缀表达式。接线员
这样我们就可以有
struct X {
int i;
auto foo() const { return X::i; }
};
其中,X::i
转换为(*this).X::i
。这是明确允许的,因为
[expr.prim.id]
表示类的非静态数据成员或非静态成员函数的id表达式只能用于:
- 作为类成员访问的一部分,其中对象表达式引用成员的类或从该类派生的类,或
(*this).X::i
的含义始终是一个左值,表示类成员i
因此,在可以计算X::i
的上下文中,它总是生成左值。因此,在这些上下文中,decltype((X::i))
必须是左值引用类型。虽然在类范围之外的X::i
通常不能在表达式中使用,但说它的值类别是左值仍然不是完全任意的。我们只是稍微扩展了一下定义区域(这与标准并不矛盾,因为标准没有定义它)
像'X::i'这样的表达式——根本无法计算,更不用说确定对象的标识了——怎么能被视为glvalue呢
忽略对«结果»的误用