`std::complex<T>[n]` 和 `T[n*2]` 类型别名
`std::complex<T>[n]` and `T[n*2]` type aliasing
因为 C++11 std::complex<T>[n]
保证可以别名为 T[n*2]
,具有明确定义的值。这正是人们对任何主流架构的期望。对于我自己的类型,这种保证是否可以通过标准 C++ 实现,比如 struct vec3 { float x, y, z; }
还是只有在编译器的特殊支持下才有可能?
大多数情况下只有编译器的特殊支持才有可能。
联合不会让你到达那里,因为通用方法实际上有未定义的行为,尽管布局兼容的初始序列有例外,并且你可以通过 unsigned char*
检查对象作为特殊情况。仅此而已。
有趣的是,除非我们假设 "below" 具有广泛且无用的含义,否则该标准在这方面在技术上是自相矛盾的:
[C++14: 5.2.10/1]:
[..] Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
complex<T>
的情况就不提了。最后,您所指的规则在很久很久以后在 [C++14: 26.4/4]
.
中引入
我认为如果您的类型包含 float x[3]
,并且您确保 sizeof(vec3) == 3*sizeof(float) && is_standard_layout_v<vec3>
,那么它可以用于单个 vec3
。鉴于这些条件,标准保证第一个成员的偏移量为零,因此第一个 float
的地址是对象的地址,您可以执行数组运算以获取数组中的其他元素:
struct vec3 { float x[3]; } v = { };
float* x = reinterpret_cast<float*>(&v); // points to first float
assert(x == v.x);
assert(&x[0] == &v.x[0]);
assert(&x[1] == &v.x[1]);
assert(&x[2] == &v.x[2]);
您不能将 vec3
的数组视为长度的三倍浮点数数组。每个 vec3
内数组的数组运算不允许您访问下一个 vec3
内的数组。 CWG 2182 与此处相关。
TL;DR:编译器必须检查 reinterpret_cast
并确定涉及 std::complex
的(标准库)特化。我们不能一致地模仿语义。
我认为很明显,将三个不同的成员视为数组元素是行不通的,因为对指向它们的指针的指针运算受到极大限制(例如,加 1 会产生 pointer past-the-end)。
所以我们假设 vec3
包含一个包含三个 int
的数组。
即使这样,您隐式需要的基础 reinterpret_cast<int*>(&v)
(其中 v
是 vec3
)也不会为您留下指向第一个元素的指针。请参阅 pointer-interconvertibility:
的详尽要求
Two objects a
and b
are pointer-interconvertible if:
they are the same object, or
one is a standard-layout union object and the other is a non-static data member of that object ([class.union]), or
one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no
non-static data members, the first base class subobject of that object
([class.mem]), or
there exists an object c
such that a
and c
are pointer-interconvertible, and c
and b
are
pointer-interconvertible.
If two objects are pointer-interconvertible, then they have the same
address, and it is possible to obtain a pointer to one from a pointer
to the other via a reinterpret_cast
. [ Note: An array object
and its first element are not pointer-interconvertible, even though
they have the same address. — end note ]
这很明确;虽然我们可以获得指向数组的指针(作为第一个成员),并且指针相互转换是可传递的,但我们无法获得指向其第一个元素的指针。
最后,即使您设法获得了指向成员数组第一个元素的指针,如果您有一个 vec3
的数组,您也无法 遍历 所有成员数组都使用简单的指针增量,因为我们得到的指针超过了数组的末尾。 launder 也没有解决这个问题,因为指针关联的对象不共享任何存储(具体请参见 [ptr.launder])。
因为 C++11 std::complex<T>[n]
保证可以别名为 T[n*2]
,具有明确定义的值。这正是人们对任何主流架构的期望。对于我自己的类型,这种保证是否可以通过标准 C++ 实现,比如 struct vec3 { float x, y, z; }
还是只有在编译器的特殊支持下才有可能?
大多数情况下只有编译器的特殊支持才有可能。
联合不会让你到达那里,因为通用方法实际上有未定义的行为,尽管布局兼容的初始序列有例外,并且你可以通过 unsigned char*
检查对象作为特殊情况。仅此而已。
有趣的是,除非我们假设 "below" 具有广泛且无用的含义,否则该标准在这方面在技术上是自相矛盾的:
[C++14: 5.2.10/1]:
[..] Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
complex<T>
的情况就不提了。最后,您所指的规则在很久很久以后在 [C++14: 26.4/4]
.
我认为如果您的类型包含 float x[3]
,并且您确保 sizeof(vec3) == 3*sizeof(float) && is_standard_layout_v<vec3>
,那么它可以用于单个 vec3
。鉴于这些条件,标准保证第一个成员的偏移量为零,因此第一个 float
的地址是对象的地址,您可以执行数组运算以获取数组中的其他元素:
struct vec3 { float x[3]; } v = { };
float* x = reinterpret_cast<float*>(&v); // points to first float
assert(x == v.x);
assert(&x[0] == &v.x[0]);
assert(&x[1] == &v.x[1]);
assert(&x[2] == &v.x[2]);
您不能将 vec3
的数组视为长度的三倍浮点数数组。每个 vec3
内数组的数组运算不允许您访问下一个 vec3
内的数组。 CWG 2182 与此处相关。
TL;DR:编译器必须检查 reinterpret_cast
并确定涉及 std::complex
的(标准库)特化。我们不能一致地模仿语义。
我认为很明显,将三个不同的成员视为数组元素是行不通的,因为对指向它们的指针的指针运算受到极大限制(例如,加 1 会产生 pointer past-the-end)。
所以我们假设 vec3
包含一个包含三个 int
的数组。
即使这样,您隐式需要的基础 reinterpret_cast<int*>(&v)
(其中 v
是 vec3
)也不会为您留下指向第一个元素的指针。请参阅 pointer-interconvertibility:
Two objects
a
andb
are pointer-interconvertible if:
they are the same object, or
one is a standard-layout union object and the other is a non-static data member of that object ([class.union]), or
one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object ([class.mem]), or
there exists an object
c
such thata
andc
are pointer-interconvertible, andc
andb
are pointer-interconvertible.If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast
. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]
这很明确;虽然我们可以获得指向数组的指针(作为第一个成员),并且指针相互转换是可传递的,但我们无法获得指向其第一个元素的指针。
最后,即使您设法获得了指向成员数组第一个元素的指针,如果您有一个 vec3
的数组,您也无法 遍历 所有成员数组都使用简单的指针增量,因为我们得到的指针超过了数组的末尾。 launder 也没有解决这个问题,因为指针关联的对象不共享任何存储(具体请参见 [ptr.launder])。