繁体   English   中英

将指向struct的指针强制转换为指向该结构的唯一成员的指针

[英]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转换为“指向cv T2指针”类型时,如果T1T2都是标准布局,则结果为static_cast<cvT2*>(static_cast<cvvoid*>(v))类型(3.9)和T2的对齐要求不比T1更严格

如果我正确解释,那么上面的代码应该是正确的,对吗? 如果没有,它可以成功吗?

(在我的回答中,我使用C ++ 14标准草案(N4140),这与C ++ 11在相关引用方面略有不同)

由于[class.mem]/19 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覆盖:

类型为“指向cv T指针”的prvalue,其中T是对象类型,可以转换为类型为“指向cv void指针”的prvalue。 将指针的非空指针值转换为对象类型的结果为“指向cv void指针”表示内存中与原始指针值相同的字节的地址。

第二个static_cast - [expr.static.cast]/13

类型为“指向cv1 void的指针”的prvalue可以转换为类型为“指向cv2 T的指针”的prvalue(...)如果原始指针值表示内存中字节的地址A并且A满足对齐要求然后,结果指针值表示与原始指针值相同的地址,即A.

由于[class.mem]/19 ,因此对齐要求得到满足,所以演员表工作正常。


但问题是似乎不能保证sizeof(foo) == sizeof(int)除了上面提到的std::complex要求。 可以将[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适当转换)指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [ 注意 :因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 - 结束说明 ]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM