简体   繁体   English

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

[英]Cast a pointer to struct to a pointer to the only member of that struct

Consider the following program: 考虑以下程序:

#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; });
}

After compiling it with Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn), it produces the following output (which is exactly what I want): 用Apple LLVM 6.0版(clang-600.0.54)(基于LLVM 3.5svn)编译后,它产生以下输出(这正是我想要的):

012345
012345

The first iteration is trivial. 第一次迭代是微不足道的。 The second iteration however is performed not through iterators, but through pointers to the underlying storage which have been cast to const int* . 然而,第二次迭代不是通过迭代器执行的,而是通过指向已经转换为const int*的底层存储的指针执行的

My question is: Is that code legal? 我的问题是: 该代码合法吗?

My intuition is that it is. 我的直觉是它。 According to §5.2.10/7 of the C++11 standard (final working draft): 根据C ++ 11标准的§5.2.10/ 7(最终工作草案):

When a prvalue v of type “pointer to T1 ” is converted to the type “pointer to cv T2 ”, the result is static_cast<cvT2*>(static_cast<cvvoid*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1 当“指向T1指针”类型的prvalue v转换为“指向cv T2指针”类型时,如果T1T2都是标准布局,则结果为static_cast<cvT2*>(static_cast<cvvoid*>(v))类型(3.9)和T2的对齐要求不比T1更严格

If I interpret this correctly, then the code above should be correct, right? 如果我正确解释,那么上面的代码应该是正确的,对吗? If not, can it be made to work? 如果没有,它可以成功吗?

(In my answer I use C++14 standard draft (N4140), which is a bit different from C++11 with regard to relevant quotes) (在我的回答中,我使用C ++ 14标准草案(N4140),这与C ++ 11在相关引用方面略有不同)

reinterpret_cast<const int*>(values.data()) is fine because of [class.mem]/19 : 由于[class.mem]/19 reinterpret_cast<const int*>(values.data()) [class.mem]/19

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. 如果标准布局类对象具有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。 (...) [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. (...)[注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 —end note ] - 尾注]

And regarding dereferencing, [expr.reinterpret.cast]/7 : 关于解除引用, [expr.reinterpret.cast]/7

An object pointer can be explicitly converted to an object pointer of a different type. 可以将对象指针显式转换为不同类型的对象指针。 When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T ”, the result is static_cast<cv T*>(static_cast<cv void*>(v)) . 当对象指针类型的prvalue v被转换为对象指针类型“指向cv T指针”时,结果是static_cast<cv T*>(static_cast<cv void*>(v))

First static_cast is covered by [conv.ptr]/2 : 第一个static_cast[conv.ptr]/2覆盖:

A prvalue of type “pointer to cv T ,” where T is an object type, can be converted to a prvalue of type “pointer to cv void ”. 类型为“指向cv T指针”的prvalue,其中T是对象类型,可以转换为类型为“指向cv void指针”的prvalue。 The result of converting a non-null pointer value of a pointer to object type to a “pointer to cv void ” represents the address of the same byte in memory as the original pointer value. 将指针的非空指针值转换为对象类型的结果为“指向cv void指针”表示内存中与原始指针值相同的字节的地址。

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

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” (...) If the original pointer value represents the address A of a byte in memory and A satisfies the alignment requirement of T, then the resulting pointer value represents the same address as the original pointer value, that is, A. 类型为“指向cv1 void的指针”的prvalue可以转换为类型为“指向cv2 T的指针”的prvalue(...)如果原始指针值表示内存中字节的地址A并且A满足对齐要求然后,结果指针值表示与原始指针值相同的地址,即A.

Alignment requirement is satisfied because of [class.mem]/19 , so the cast works fine. 由于[class.mem]/19 ,因此对齐要求得到满足,所以演员表工作正常。


But the problem is that there seem to be no guarantee that sizeof(foo) == sizeof(int) except aforementioned requirement for std::complex . 但问题是似乎不能保证sizeof(foo) == sizeof(int)除了上面提到的std::complex要求。 One can interpret the note about unnamed padding from [class.mem]/19 as allowing padding only if it's needed for alignment, so there must not be any padding in your case, but in my opinion that note is too vague in this regard. 可以将[class.mem]/19关于未命名填充的注释解释为仅在需要对齐时允许填充,因此在您的情况下不得有任何填充,但在我看来,这方面的注释太模糊了。

What you can do is put in your code 您可以做的是放入您的代码

static_assert(sizeof(foo) == sizeof(int), "");
// this may be paranoic but won't hurt
static_assert(alignof(foo) == alignof(int), "");

So at least your code won't compile if the requirements are violated. 因此,如果违反要求,至少您的代码将无法编译。

It is correct. 它是正确的。 A pointer to a struct may be cast to a pointer to it's first member, under certain conditions which are met here. 在某些条件下,指向结构的指针可以转换为指向它的第一个成员的指针。 It's a legacy holdover from C, as this was how totally-not-inheritance was implemented back then. 这是C的遗留保留,因为这是当时实现完全不继承的方式。

This is specified in C++11 §9.2/20 [class.mem]: 这在C ++11§9.2/ 20 [class.mem]中指定:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast , points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. 指向标准布局结构对象的指针(使用reinterpret_cast适当转换)指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [ Note : There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. [ 注意 :因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 end note ] - 结束说明 ]

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

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