如何在 C++20 中安全地将 const char* 转换为 const char8_t*?

问题描述 投票:0回答:2

这个答案我了解到,在C++17中我们可以通过

std::fstream
使用UTF-8路径打开
std::filesystem::u8path
。但在 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
的视图?

c++ unicode utf-8 c++20
2个回答
7
投票

免责声明:我是 P0482 提案的作者,该提案引入了

char8_t
并弃用了
u8path

您的观察是正确的;不允许使用

reinterpret_cast
生成指向
char8_t
对象序列的
char
指针。 https://stackoverflow.com/a/57453713/11634221进一步讨论了这一点。

std::filesystem::u8path
在 C++20 中已弃用。但是,尽管存在 P3364 建议在 C++26 中删除它,但您可以继续使用它。即使它被删除,
std::filesystem::u8path
名称也将保留在[zombie.names]中,以便标准库实现者可以根据需要继续提供它以实现向后兼容目的。

P1423纠正了P0482中的更改的意外后果;在 C++20 中,可以使用

std::filesystem::u8path
char
的范围来调用
char8_t

没有(格式良好的)方法来生成

char8_t
序列的基于
char
指针的视图。 可以编写一个转换视图适配器,在内部将各个
char
值转换为迭代器取消引用时的
char8_t
。 这样的适配器可以满足 C++17 和 C++20 随机访问迭代器对不可变迭代器的要求(它不能满足可变迭代器的要求,因为取消引用操作无法提供左值,也不能满足连续迭代器的要求)。 这样的适配器足以调用接受范围的
std::filesystem::path
构造函数;请参阅 https://godbolt.org/z/hsYWe6qbo 作为示例。

#include <filesystem>
#include <ranges>
#include <string>

std::filesystem::path my_u8path(const std::string &s) {
  auto u8view = std::ranges::views::transform(s, [](char c) -> char8_t { return c; });
  return std::filesystem::path{u8view.begin(), u8view.end()};
}

查看底层

char
数据的另一种方法当然是复制它,但我可以理解为什么这样做可能被认为是不可取的(我们在使用
std::filesystem::path
时已经倾向于进行大量复制) .


2
投票

来自此字符类型参考关于

char8_t

它与

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

因为它是一个不同类型,所以在不破坏严格别名的情况下,您无法从

const char*
转换为
const char8_t*
。但出于所有实际目的,由于
char8_t
基本上是
unsigned char
,因此您可以使用
reinterpret_cast
来转换指针。这是错误的,但会起作用。

为了确保正确性,请使用

char8_t
开始,或将原始字符复制到
char8_t
缓冲区(或
std::u8string
)中。

© www.soinside.com 2019 - 2024. All rights reserved.