我有一个类型为std::wstring
的对象和一个以const uint32_t*
为参数的库函数。我的代码是运行在平台上的sizeof(wchar_t) == 4
。库(harfbuzz)中没有采用const wchar_t*
的API,因此我必须转换std::wstring
。我知道reinterpret_cast
是不安全的(它违反了严格的混叠规则),但我不想创建std::vector<uint32_t>
,调整它的大小和memcpy
我的整个字符串。我100%肯定编译器不会优化它。而且std::bit_cast
似乎也帮不了我,它只能将单个wchar_t
转换为uint32_t
。
我看到只有一个解决方案:使用reinterpret_cast
。字符串不会在我的代码中改变,也不会在库中改变,所以我不认为在这种情况下违反严格的混叠规则是一个问题。
还有其他选择吗?当代码中的数据类型与某些c库(int32_t*
vsuint32_t*
,char16_t*
vsunsigned short*
等)中的输入类型不匹配时,您该怎么办?
在标准c++中避免类型混叠违反和避免复制的唯一可能的方法是重用std::wstring
缓冲区的存储和"create"std::uint32_t
的数组
template<class Dst, class Src>
Dst*
reinterpret(std::span<Src> src) noexcept
{
static_assert(sizeof(Src) == sizeof(Dst));
static_assert(std::is_trivial_v<Src>);
static_assert(std::is_trivial_v<Dst>);
Src* storage = src.data();
std::size_t size = src.size();
Dst value = src[0];
Dst* result = ::new (storage) Dst(value);
for(std::size_t i = 1; i < size; i++) {
Dst value = std::bit_cast<Dst>(src[i]);
::new (storage + i) Dst(value);
}
return result;
}
这有效地不对数据进行更改,而只是实现了规避类型混叠。一个好的优化器将函数编译为:
mov rax, rdi
ret
缺点是你依赖优化器来完成它的工作。不能保证循环被优化掉了。
这基本上是建议的std::start_lifetime_as_array
库函数应该做的。
一种非标准的替代方法是以牺牲其提供的优化为代价禁用类型混叠限制。假设你的编译器支持这个选项。
注:数组位置new不能用于此目的(可能没有任何目的)。
如果您需要严格遵守标准,唯一的选择是从std::wstring
上的c_str()
memcopy到uint32_t
数组。你可以喊@#!严格的混叠规则,如果它能让你平静一点…
如果你只是一个使用真实世界编译器的真实世界的程序员,他们中的大多数(不是全部)都知道严格的混叠规则有时可能有点过于严格,并提供(作为文档扩展)一个释放它的选项。我的建议是在你使用滥用强制转换(C风格强制转换或reinterpret_cast)的编译单元上使用该选项,但要使编译单元尽可能小,仍然允许优化编译器在所有其他编译单元上假设严格混叠。