严格别名规则

Ada*_*dam 5 c++ strict-aliasing

我正在读关于reinterpret_cast及其别名规则的说明(http://en.cppreference.com/w/cpp/language/reinterpret_cast).

我写了那段代码:

struct A
{
  int t;
};

char *buf = new char[sizeof(A)];

A *ptr = reinterpret_cast<A*>(buf);
ptr->t = 1;

A *ptr2 = reinterpret_cast<A*>(buf);
cout << ptr2->t;
Run Code Online (Sandbox Code Playgroud)

我认为这些规则不适用于此:

  • T2是对象的(可能是cv限定的)动态类型
  • T2和T1都是(可能是多级,可能在每个级别的cv限定)指向相同类型T3的指针(因为C++ 11)
  • T2是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,包含的联合的子聚合和非静态数据成员的元素):这使得它可以安全地进行转换从结构的第一个成员,从union的一个元素到包含它的struct/union.
  • T2是对象的动态类型的(可能是cv限定的)有符号或无符号变体
  • T2是对象的动态类型的(可能是cv限定的)基类
  • T2是char或unsigned char

在我看来,这段代码是不正确的.我对吗?代码是否正确?

另一方面,连接功能(man 2 connect)和struct sockaddr怎么样?

   int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);
Run Code Online (Sandbox Code Playgroud)

例如.我们有struct sockaddr_in,我们必须将它转换为struct sockaddr.以上规则也不适用,这个演员是不正确的?

Lig*_*ica 5

是的,它是无效的,但不是因为你正在将a转换char*A*:它是因为你没有获得A*实际指向的a ,A*并且正如你所确定的那样,没有任何类型的别名选项适合.

你需要这样的东西:

#include <new>
#include <iostream>

struct A
{
  int t;
};

char *buf = new char[sizeof(A)];

A* ptr = new (buf) A;
ptr->t = 1;

// Also valid, because points to an actual constructed A!
A *ptr2 = reinterpret_cast<A*>(buf);
std::cout << ptr2->t;
Run Code Online (Sandbox Code Playgroud)

现在输入别名根本不会进入它(虽然继续阅读因为还有更多要做!).

实际上,这还不够.我们还必须考虑调整.虽然上面的代码似乎可以工作,但是要完全安全,你需要将它放置new到一个正确对齐的存储区域,而不仅仅是一个偶然的块char.

标准库(从C++ 11开始)使我们std::aligned_storage能够这样做:

using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;
auto* buf = new Storage;
Run Code Online (Sandbox Code Playgroud)

或者,如果您不需要动态分配它,只需:

Storage data;
Run Code Online (Sandbox Code Playgroud)

然后,做你的位置 - 新:

new (buf) A();
// or: new(&data) A();
Run Code Online (Sandbox Code Playgroud)

并使用它:

auto ptr = reinterpret_cast<A*>(buf);
// or: auto ptr = reinterpret_cast<A*>(&data);
Run Code Online (Sandbox Code Playgroud)

所有内容看起来像这样:

#include <iostream>
#include <new>
#include <type_traits>

struct A
{
  int t;
};

int main()
{
    using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;

    auto* buf = new Storage;
    A* ptr = new(buf) A();

    ptr->t = 1;

    // Also valid, because points to an actual constructed A!
    A* ptr2 = reinterpret_cast<A*>(buf);
    std::cout << ptr2->t;
}
Run Code Online (Sandbox Code Playgroud)

(现场演示)

即便如此,自C++ 17以来,这有点复杂; 有关更多信息,请参阅相关的cppreference页面并注意std::launder.

当然,这整件事情似乎是人为的,因为你只需要一个A,因此不需要数组形式; 事实上,你首先要创建一个沼泽标准A.但是,假设buf实际上实际上更大,并且你正在创建一个分配器或类似的东西,这是有道理的.

  • 有关SO的一些讨论是否在安置后的警告是误报 (2认同)