C++11 中的严格别名规则

Strict aliasing rule in C++11

我在我的C++11代码中使用了以下C结构(代码来自PostGis的liblwgeom,但这不是问题的核心)。使用 g++-4.8 使用以下选项编译代码:

-std=c++11 -Wall -Wextra -pedantic-errors -pedantic -Werror

而且我在编译过程中没有收到任何错误(或警告)(我应该收到任何错误吗?)

问题

在接受 LWGEOM 且不修改 void *data; 成员的函数中使用 LWPOLY(实际上由 LWGEOM* 指出)是安全的。我知道这是穷人的遗产,但这是我需要处理的。

详情

多边形:

typedef struct
{
        uint8_t type; /* POLYGONTYPE */
        uint8_t flags;
        GBOX *bbox;
        int32_t srid;
        int nrings;   /* how many rings we are currently storing */
        int maxrings; /* how many rings we have space for in **rings */
        POINTARRAY **rings; /* list of rings (list of points) */
}
LWPOLY; /* "light-weight polygon" */

LWGEOM:

typedef struct
{
        uint8_t type;
        uint8_t flags;
        GBOX *bbox;
        int32_t srid;
        void *data;
}
LWGEOM;

点阵列:

typedef struct
{
        /* Array of POINT 2D, 3D or 4D, possibly missaligned. */
        uint8_t *serialized_pointlist;

        /* Use FLAGS_* macros to handle */
        uint8_t  flags;

        int npoints;   /* how many points we are currently storing */
        int maxpoints; /* how many points we have space for in serialized_pointlist */
}
POINTARRAY;

GBOX:

typedef struct
{
        uint8_t flags;
        double xmin;
        double xmax;
        double ymin;
        double ymax;
        double zmin;
        double zmax;
        double mmin;
        double mmax;
} GBOX;

我这样做是否违反了严格的别名规则?

const LWGEOM* lwgeom;
...
const LWPOLY* lwpoly = reinterpret_cast<const LWPOLY*>(lwgeom);

我知道在 PostGis 中类型被专门设计为 "compatible" 但是我想知道我这样做是否违反了标准。

此外,我注意到 PostGis 编译时默认禁用严格别名(至少版本 2.1.5)。

解决方案

我的同事帮我调查了一下,答案似乎是否定的,它没有违反严格的别名,但只有在我们访问与 LWPOLY 类型相同且布局在结构的开头连续。这就是为什么(引用标准):

3.10.10 表示您可以通过指向 "aggregate or union".

的指针访问成员

8.5.1 定义聚合(C 结构是聚合): 聚合是一个数组或 class(第 9 条),没有用户提供的构造函数 (12.1),没有私有或 受保护的非静态数据成员(第 11 条),无基 classes(第 10 条),无虚函数 (10.3).

9.2.19 表示指向结构的指针与指向标准布局的第一个成员的指针相同 classes(C 结构是标准布局)。

这是否是一种安全的编码方式是另一个问题。

是的,它违反了严格的别名规则。 LWGEOMLWPOLY是不相关的类型,intvoid*也是。因此,例如,对 lwgeom->data 的修改可能无法通过 lwpoly->nrings 读取,反之亦然。

我用 GCC4.9 验证了这一点。我的代码如下:

#include <cinttypes>
#include <iostream>

using namespace std;

typedef struct {
        uint8_t type; /* POLYGONTYPE */
        uint8_t flags;
        int32_t srid;
        int nrings;   /* how many rings we are currently storing */
} LWPOLY; /* "light-weight polygon" */

typedef struct {
        uint8_t type;
        uint8_t flags;
        int32_t srid;
        void *data;
} LWGEOM;

void f(LWGEOM* pgeom, LWPOLY* ppoly) {
    ppoly->nrings = 7;
    pgeom->data = 0;
    std::cout << ppoly->nrings << '\n';
}

int main() {
    LWGEOM geom = {};
    LWGEOM* pgeom = &geom;
    LWPOLY* ppoly = (LWPOLY*)pgeom;
    f(pgeom, ppoly);
}

你猜怎么着,输出是 7。