在不违反严格别名的情况下将 `u8string_view` 转换为 `char` 数组?

sml*_*mls 8 c++ strict-aliasing undefined-behavior c++20

前提

  • 我在内存中有一个二进制数据块,表示为char*(可能从文件中读取,或通过网络传输)。
  • 我知道它在某个偏移量处包含一个特定长度的 UTF8 编码文本字段。

我如何(安全且可移植地)获得一个u8string_view来表示此文本字段的内容?

动机

将该字段作为 a 传递给下游代码的动机u8string_view是:

  • 它非常清楚地表明文本字段是 UTF8 编码的,与string_view.
  • 它避免了将其作为u8string.

我试过的

这样做的天真方法是:

char* data = ...;
size_t field_offset = ...;
size_t field_length = ...;

char8_t* field_ptr = reinterpret_cast<char8_t*>(data + field_offset);
u8string_view field(field_ptr, field_length);
Run Code Online (Sandbox Code Playgroud)

但是,如果我正确理解 C++ 严格别名规则,这是未定义的行为,因为它char*通过char8_t*返回的指针访问缓冲区的内容reinterpret_cast,而char8_t不是别名类型。

真的吗?

有没有办法安全地做到这一点?

Oli*_*liv 1

当您访问具有不可接受类型的泛左值的对象时,会发生严格的别名规则。

首先考虑一个明确定义的情况:

char* data = reinterpret_cast <char *> (new char8_t[10]{})
size_t field_offset = 0;
size_t field_length = 10;
char8_t* field_ptr = reinterpret_cast<char8_t*>(data + field_offset);
u8string_view field(field_ptr, field_length);
field [0]+field[1];
Run Code Online (Sandbox Code Playgroud)

这里没有UB。您创建一个数组,char8_t然后访问该数组的元素。

现在,如果所引用的内存对象是data由另一个程序创建的,会发生什么情况?根据标准,这是 UB,因为该对象不是通过指定的创建方式之一创建的

但您的代码尚未受到标准支持这一事实在这里并不是问题。所有编译器都支持此代码。如果不是,则什么都不起作用,您甚至无法执行最简单的系统调用,因为程序和任何内核之间的大多数通信都是通过字符数组进行的。因此,只要在程序内部访问位于类型的泛左值之间data+field_offset并通过该类型的内存,您的代码就会按预期工作。data+field_offset+field_lengthchar8_t