如何通过聚合访问对象的存储
How to access an object's storage through an aggregate
在"Lvalues and 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
类型的聚合类型"的gl值,从而使其有效?
该列表的目的不是为您提供访问对象的替代方法,而是如列表脚注所示,列出对象可能被别名的所有方式。 请考虑以下示例:
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
的指针"可以显式 使用reinterpret_cast
转换为类型"指向T2
的指针"。那 是,引用强制转换reinterpret_cast<T&>(x)
具有与 转换*reinterpret_cast<T*>(&x)
内置&
和*
运算符(对于reinterpret_cast<T&&>(x)
也是如此(。结果 引用与源左值相同的对象,但具有不同的 类型。结果是左值引用类型的左值或 对函数类型的 R 值引用和 R 值的 xValue 对对象类型的引用。没有临时是 已创建,不创建副本,构造函数 不调用 (12.1( 或转换函数 (12.3(。7171 这有时被称为双关语类型。
在您的示例上下文中,它是说如果将指针int
转换为指向Foo
的指针是合法的,那么您的reinterpret_cast<Foo&)(a)
是合法的并产生一个左值。 (第1段告诉我们这将是一个左值。 而且,正如我所读到的,根据第 7 段,指针转换本身是可以的:
指向对象的指针可以显式转换为指向 不同的对象类型。当类型为"指向
T1
的指针"的 prvaluev
转换为类型"指向 CVT2
的指针",如果T1
和T2
都是标准布局类型 (3.9( 并且对齐方式都static_cast<cv T2*>(static_cast<cv void*>(v))
T2
的要求并不比T1
严格。将类型为"指向T1
的指针"的 prvalue 转换为 类型"指向T2
的指针"(其中T1
和T2
是对象类型,其中T2
的对齐要求为否 比T1
的更严格,并返回到其原始类型,将生成原始指针值。任何结果 其他此类指针转换未指定。
您具有具有兼容对齐约束的标准布局类型。 所以,你有一个产生左值的类型双关语。 您列出的规则本身不会使其未定义。
那么,是什么可能使它未定义呢? 首先,[class.mem] (9.2( 第 21 段提醒我们,指向标准布局结构对象的指针指向其初始成员,反之亦然。 因此,在键入双关语之后,您留下了对Foo
的引用,这样Foo
的x
与a
位于同一位置。
而且......这就是我的语言律师逐渐消失的地方。 我知道,通过该弗兰肯引用访问Foo
充其量是已定义或未指定的实现。 我找不到它在哪里被明确放逐到未定义行为的领域。
但是,我想我回答了你最初的问题:为什么聚合规则在那里? 它为您提供了一种非常基本的方式来裁定潜在的别名,而无需进一步的指针分析。
子句的项仅指对任何聚合(struct
、class
或数组(或union
的成员的正常访问: 您需要能够访问对象的存储值,而不会导致未定义的行为。该条款仅规定了必要条件:至少其中一项必须为真。它没有说明充分的条件,即除了这些条件之外,其他条件也可能需要成立。
- C++ 在堆栈中包含多态属性的类对象存储
- 将对象存储为其基本虚拟类
- 将大型对象存储在无序映射中是否效率低下
- C++:将未知类型的对象存储为成员变量的类
- 按unique_pointer将传递的对象存储在地图中
- 如何将 c++ 类对象存储为数组?
- 将对象存储在 std::map 中
- 是否存在对象存储在其生存期内可能会更改的情况?
- 将不同类型的对象存储在容器中
- 将自定义可绘制对象存储在数组中会导致绘制时出现分割错误
- 我将如何在C++中文件处理对象(存储/导入)
- 将对象存储在C 中的2维Sprite矩阵中
- 将抽象派生类对象存储在基类向量中
- 将任意函数对象存储到类成员容器中,而无需知道其声明签名
- 如何使用向量将类对象存储在超载操作员中
- 适当的方法将对象存储在Std矢量中
- 将向量对象存储在共享指针投掷错误中
- 将多态对象存储在unordered_set中
- 为什么我可以将对象存储在大小不同的数组中
- 返回对象存储在哪里