【问题标题】:How to safely convert const char* to const char8_t* in C++20?如何在 C++20 中安全地将 const char* 转换为 const char8_t*?
【发布时间】:2019-12-27 09:42:54
【问题描述】:

this answer 我了解到,在 C++17 中,我们可以通过 std::filesystem::u8path 使用 UTF-8 路径打开 std::fstream。但在 C++20 中,该函数已被弃用,我们应该将 const char8_t* 传递给 std::filesystem::path 构造函数。

问题来了:虽然我们可以合法地转换(通过reinterpret_cast)任何指向const char* 的指针,但我们不能倒转:从const char* 到例如const char8_t*(它会破坏严格的别名规则)。因此,如果我们有一些外部 API 返回文件名的基于 char 的 UTF-8 表示(例如,来自用 C 编写的库),我们不能安全地将指针转换为基于 char8_t 的指针。

那么,我们应该如何将这种基于char 的UTF-8 字符串视图转换为基于char8_t 的视图呢?

【问题讨论】:

  • “我们不能倒退”。你为什么想要? UTF-8 字符串数据应以char8_t 字符串开头。
  • @n.m.不应该意味着它确实如此。在 2020 年之前设计的任何数量的库甚至都不知道 char8_t
  • 旧库可能会继续使用旧 API。已弃用并不意味着已删除。
  • 我想我们应该等到标准最终确定并且编译器实现了与char8_t 相关的所有内容。我敢打赌,演员阵容会保存在 MSVC 上,因为据我所知,它没有利用 SAR,并且 gcc&clang 将有一些开关将char8_t 视为char 以允许别名。 UB 就是 UB,但它与 SAR 和 legacy->new code bridges 是这样的。
  • 如果char[] 数据已经以UTF-8 编码,那么只需将memcpy()std::copy() 原样放入char8_t[] 缓冲区。

标签: c++ unicode utf-8 c++20


【解决方案1】:

免责声明:我是 P0482 提案的作者,该提案引入了 char8_t 并弃用了 u8path

你的观察是正确的;不允许使用reinterpret_cast 生成指向char 对象序列的char8_t 指针。这将在https://stackoverflow.com/a/57453713/11634221 进一步讨论。

虽然std::filesystem::u8path 在 C++20 中已被弃用,但没有计划立即删除它;你可以继续使用它。此外,P1423 更正了 P0482 中更改的意外后果,并允许在 C++20 中使用charchar8_t 的范围调用它。据我所知,没有实现者将std::filesystem::u8path 注释为已弃用(我不知道是否有这样做的计划)。

没有(格式良好的)方法可以生成基于 char8_t 指针的 char 序列视图。可以编写一个范围/迭代器适配器,在内部将单个 char 值转换为迭代器取消引用时的 char8_t。这样的适配器可以满足 C++17 和 C++20 随机访问迭代器对非可变迭代器的要求(它不能满足对可变迭代器的要求,因为解引用操作无法提供一个左值,也不能满足连续迭代器的要求)。这样的适配器足以调用接受范围的std::filesystem::path 构造函数。嗯,这可能是一个足够有用的适配器,可以添加到https://github.com/tahonermann/char8_t-remediation

查看底层 char 数据的替代方法当然是复制它,但我可以理解为什么这样做可能被认为是不可取的(我们在使用 @987654341 时已经倾向于进行大量复制@)。

【讨论】:

  • std::u8string_view 应该是这个适配器。它是不可变的,其初衷是让用户不必关心底层的字符串存储。为什么我们不能有好东西:(?
  • 使std::u8string_view 适配器需要在其实现中使用reinterpret_cast(或类似的)。那将阻止std::u8string_view 成为constexpr。此外,即使我们采用某种编译器魔法来实现这项工作,除非我们还将这种魔法扩展到 std::span 和其他未来类型,如 std::text_view,否则这将是一个奇怪的一次性。
【解决方案2】:

来自this character types reference关于char8_t

它与unsigned char 具有相同的大小、符号和对齐方式(因此,与charsigned char 具有相同的大小和对齐方式),但是是不同的类型。

因为它是一个独特的类型,你不能在不破坏严格别名的情况下从const char* 转换为const char8_t*。但出于所有实际目的,由于char8_t 基本上是unsigned char,您可以使用reinterpret_cast 来转换指针。这是错误的,但会起作用。

为确保正确性,请使用char8_t 开头,或将原始字符复制到char8_t 缓冲区(或std::u8string)中。

【讨论】:

  • 大小、签名和对齐并不是让我们违反严格的别名规则的原因。
  • 由于它是一种独特的类型,因此您无法访问强制转换指针,因为它违反了严格的别名规则。
猜你喜欢
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
  • 2019-11-11
相关资源
最近更新 更多