从这个答案我了解到,在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
的视图?
免责声明:我是 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
时已经倾向于进行大量复制) .
来自此字符类型参考关于
char8_t
:
它与
具有相同的大小、符号和对齐方式(因此,与unsigned char
和char
具有相同的大小和对齐方式),但是是一种不同的类型。signed char
因为它是一个不同类型,所以在不破坏严格别名的情况下,您无法从
const char*
转换为 const char8_t*
。但出于所有实际目的,由于 char8_t
基本上是 unsigned char
,因此您可以使用 reinterpret_cast
来转换指针。这是错误的,但会起作用。
为了确保正确性,请使用
char8_t
开始,或将原始字符复制到 char8_t
缓冲区(或 std::u8string
)中。