C++ 在C+中,未赋值的非静态数据成员的左值类别的合理性是什么+;?

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命名的实体的类型。如果没有这样的实体,

gcc和clang都接受下面的代码,我正试图找出原因

<代码> //C++ + STD= C++ 20 -墙-C测试 #包括 结构X{ int i; }; //这是语言规范明确要求的: 静态断言(标准::与相同); //这似乎更加武断: 静态断言(标准::与相同); 根据[dcl.type.decltype],第一行
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呢

忽略对«结果»的误用