在“Lvalues和rvalues”,[basic.lval](3.10)中,C ++标准包含一个类型列表,使得通过这种类型的glvalue“访问对象的存储值”是有效的(第10段)。具体来说,它说:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
对象的动态类型
[关于简历和签名/未签名的一些不重要的细节]
聚合或联合类型,其中包含其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),< /强>
[更多东西]
“聚合”规则究竟是什么意思?如何通过某些常规聚合类型的glvalue访问对象的存储值?!
我正在想象这样的事情:
int a = 10; // my "stored value"
struct Foo { char x; float y; int z; bool w; }; // an aggregate
reinterpret_cast<Foo&>(a).y = 0; // ???
最终演员表是否会产生“包含动态类型a
”的聚合类型的glvalue,从而使其有效?
答案 0 :(得分:7)
该列表的目的不是为您提供访问对象的替代方法,而是如列表的脚注所示,列出对象可能具有别名的所有方式。请考虑以下示例:
struct foo
{
char x;
float y;
int z;
bool w;
};
void func( foo &F, int &I, double &D )
{
//...
}
该列表所说的是对F
的访问也可以访问与I
的访问相同的底层对象。如果您在F.z
中传递了对I
的引用,可能会发生这种情况,如下所示:
func(F, F.z, D);
另一方面,您可以安全地假设F
无法访问与D
相同的基础对象,因为struct foo
不包含double
类型的任何成员
即使有些小丑这样做也是如此:
union onion
{
struct foo F;
double D;
};
onion o;
int i;
func( o.F, i, o.D ); // [class.union] (9.5) wants a word with you. UB.
我不确定union
是您问题的核心所在。但是union
示例之前的部分突出显示了聚合规则存在的原因。
现在让我们考虑你的例子:reinterpret_cast<Foo&>(a).y = 0;
[expr.reinterpret.cast](5.2.10),第11段有这样说:
类型
T1
的左值表达式可以转换为“引用”类型T2
“如果可以明确表达”T1
指针“类型的表达式 使用T2
转换为“指向reinterpret_cast
”的类型。那 是,参考演员reinterpret_cast<T&>(x)
具有相同的效果 内置*reinterpret_cast<T*>(&x)
和&
的转化*
运算符(类似于reinterpret_cast<T&&>(x)
)。结果 引用与源左值相同的对象,但具有不同的对象 类型。结果是左值引用类型的左值或 rvalue对函数类型的引用和对rvalue的xvalue 对象类型的引用。暂时没有 创建,没有副本和构造函数 (12.1)或转换函数(12.3)不被称为。 71
71 这有时被称为式双关语。
在您的示例的上下文中,它表示如果将指针转换为 - int
指针指向 - Foo
是合法的,那么您的reinterpret_cast<Foo&)(a)
是合法的,产生一个左值。 (第1段告诉我们它将是一个左值。)而且,正如我读到的那样,指针转换本身就没问题,根据第7段:
指向对象的指针可以显式转换为指向a的指针 不同的对象类型。当“
v
指针”类型的prvalueT1
为时 转换为“指向 cvT2
的指针”,如果static_cast<cv T2*>(static_cast<cv void*>(v))
和T1
都是标准布局类型,则结果为T2
(3.9 )和对齐T2
的要求并不比T1
的要求更严格。将“指针T1
”类型的prvalue转换为 输入“指向T2
的指针”(其中T1
和T2
是对象类型,T2
的对齐要求为no 比T1
更严格,并返回其原始类型产生原始指针值。任何结果 其他此类指针转换未指定。
您具有兼容对齐约束的标准布局类型。所以,你所拥有的是一个产生左值的类型双关语。您列出的规则不是自己使其未定义。
那么可能会使它未定义?好吧,对于一个,[class.mem](9.2)第21段提醒我们,指向标准布局结构对象的指针指向其初始成员,反之亦然。因此,在您的类型双关语后,您会留下Foo
的引用,以使Foo
的{{1}}与x
位于同一位置。
而且......这是我的语言律师逐渐消失的地方。我知道,通过该franken-reference访问a
是在最佳实现定义或未指定。我无法找到明确放逐到 undefined 行为领域的地方。
但是,我想我回答了你原来的问题:为什么汇总规则存在?它为您提供了一种非常基本的方法来规划潜在的别名而无需进一步的指针分析。
答案 1 :(得分:2)
该条款的项目仅指对任何聚合(struct
,class
或数组)或union
的成员的正常访问:您需要能够访问存储的对象值而不会导致未定义的行为。该条款仅规定了必要条件:至少有一个项目必须是真实的。它没有说明足够的条件,即除了这些条件之外,其他条件也可能需要保持。