将指向结构的指针强制转换为指向该结构的唯一成员的指针
Cast a pointer to struct to a pointer to the only member of that struct
请考虑以下程序:
#include <algorithm>
#include <iostream>
#include <vector>
struct foo {
foo(int value)
: value_(value)
{
// perform range checks
}
int value() const {
return value_;
}
private:
int value_;
};
int main() {
std::vector<foo> values{0, 1, 2, 3, 4, 5};
std::for_each(std::begin(values), std::end(values),
[](foo& f){ std::cout << f.value(); });
std::cout << std::endl;
std::for_each(reinterpret_cast<const int*>(values.data()),
reinterpret_cast<const int*>(values.data()) + values.size(),
[](int i){ std::cout << i; });
}
使用 Apple LLVM 版本 6.0 (clang-600.0.54)(基于 LLVM 3.5svn) 编译后,它会产生以下输出(这正是我想要的):
012345
012345
第一次迭代是微不足道的。但是,第二次迭代不是通过迭代器执行的,而是通过指向已强制转换为const int*
的基础存储的指针执行的。
我的问题是:该代码合法吗?
我的直觉是这样。根据 C++11 标准(最终工作草案)的 §5.2.10/7
:当类型为"指向
严格T1
的指针"的 prvalue v 转换为类型 "指向 cvT2
的指针"static_cast<cvT2*>(static_cast<cvvoid*>(v))
,如果T1
和T2
都是 标准布局类型 (3.9) 和T2
的对齐要求是 不比T1
如果我正确解释了这一点,那么上面的代码应该是正确的,对吧?如果没有,它能起作用吗?
(在我的回答中,我使用C++14标准草案(N4140),在相关引号方面与C++11略有不同)
reinterpret_cast<const int*>(values.data())
很好,因为[class.mem]/19
:
如果标准布局类对象具有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。(...)[ 注意:因此,标准布局结构对象中可能存在未命名的填充,但在其开头没有,这是实现适当对齐所必需的。
关于取消引用,[expr.reinterpret.cast]/7
:
对象指针可以显式转换为不同类型的对象指针。当对象指针类型的 prvalue v 转换为对象指针类型"指向 cv
T
的指针"时,结果为static_cast<cv T*>(static_cast<cv void*>(v))
。
第一个static_cast
由[conv.ptr]/2
涵盖:
类型为"指向 cvT
的指针"的 prvalue,其中
T
是对象类型,可以转换为类型为"指针"的 prvalue 到简历void
"。将指向对象类型的指针的非 null 指针值转换为"指针指向"的结果 cvvoid
" 表示内存中与原始指针值相同的字节的地址。
第二static_cast
- [expr.static.cast]/13
:
类型为"指向 cv1 void 的指针"的 prvalue 可以转换为"指向 cv2 T 的指针"类型的 prvalue,(...)如果原始指针值表示内存中某个字节的地址 A,并且 A 满足 T 的对齐要求,则生成的指针值表示与原始指针值相同的地址,即 A。
由于[class.mem]/19
,对齐要求得到满足,所以铸件工作正常。
但问题是,除了上述std::complex
要求外,似乎无法保证sizeof(foo) == sizeof(int)
。人们可以将[class.mem]/19
中关于未命名填充的注释解释为仅在对齐需要时才允许填充,因此在您的情况下不得有任何填充,但在我看来,该注释在这方面太模糊了。
你可以做的是放入你的代码中
static_assert(sizeof(foo) == sizeof(int), "");
// this may be paranoic but won't hurt
static_assert(alignof(foo) == alignof(int), "");
因此,如果违反要求,至少您的代码不会编译。
这是正确的。指向结构的指针可以强制转换为指向其第一个成员的指针,在此满足某些条件。这是 C 的遗留遗留物,因为这是当时完全非继承的实现方式。
这在 C++11 §9.2/20 [class.mem] 中指定:
指向标准布局结构对象的指针,使用
reinterpret_cast
指向其初始成员(或者如果该成员是 位域,然后是它所在的单元),反之亦然。[ 注意:因此,在 标准布局结构对象,但不是在其开头,根据需要 以实现适当的对齐。— 尾注 ]
- 如何在 C# 中映射双 C 结构指针?
- 指针或结构的矢量
- MSVC将仅移动结构参数解释为指针
- 如何访问指针结构的成员变量?
- 如何将变量从变量传递到指针结构对象
- 如何在函数中检索指针结构
- C :变量的点地址至指针结构成员的地址
- C++指针结构不会被删除
- 初始化指针结构 - 内存中的外观
- C++指针结构和多线程
- c++中指针结构的运算符重载
- 为什么我不能转发声明指针结构?
- C# 如何定义新的指针结构
- 如何处理大型指针结构
- 我怎么能写一个函数的定义在模板类中声明返回指针结构对象
- 我如何编程指针结构或类变量在c++
- 具有指针结构的类所需的析构函数
- 为什么在非指针结构上进行类型转换会出现语法错误
- 语法:做binary_search (STL)排序向量的指针结构
- /a.检测到Glibc.out: munmap_chunk():无效指针- c++结构体的动态数组