将 std::u16string 转换为 std::wstring,无需复制
Convert std::u16string to std::wstring without copy
我用下面的接口做了一堆UTF转换函数:
template <typename T, typename U> std::basic_string<T> UTFConvert(std::basic_string_view<U> a_String);
有char
、char16_t
和char32_t
的所有组合的实现。但是现在我也需要添加对wchar_t
的支持。我知道编译时wchar_t
的大小,所以理论上我可以用相同大小的字符调用函数。
问题是我将不得不将结果字符串复制回std::wstring
.例如,如果sizeof(wchar_t) == 2
我最终会做这样的事情:
template <typename T, typename U>
std::enable_if_t<std::is_same_v<T, wchar_t>, std::basic_string<T>> UTFConvert(std::basic_string_view<U> a_String)
{
const std::u16string utf16 = UTFConvert<char16_t>(a_String);
std::wstring wstr;
wstr.resize(utf16.size());
memcpy(wstr.data(), utf16.data(), utf16.size() * sizeof(wchar_t));
return wstr;
}
像这样复制字符串似乎有点浪费。有没有办法避免这种情况,而无需为不同类型的重新实现相同的代码两次?
这个问题已经通过专门针对 T 的大小而不是特定的字符类型来解决:
template <typename T, typename U>
std::enable_if_t<sizeof(T) == 2, std::basic_string<T>> UTFConvert(std::basic_string_view<U> a_String);
只需在那里撒上几static_assert
以保持理智,一切都很完美!
谢谢@MassimilianoJanes的建议。
你处于C++的一个角落,标准有一些粗糙的地方。
这里有一些理论上的陷阱。
首先,在C++char16_t
缓冲区上取消引用wchar_t*
是不合法的,反之亦然。 此问题称为"严格别名"。
如果您有固定大小的缓冲区,则可以通过仔细来回复制和构造在有限的范围内解决此问题。 但是,C++ 标准中存在一个缺陷,使得创建动态大小的数组无法"手动"完成,而无需对所讨论的类型调用new[]
(根据用户代码的标准,不可能实现std::vector
或类似)。
这是标准中的一个缺陷,但据我所知,目前尚未解决。
所以问题就变成了,你想在多大程度上遵循标准,你有多想要一些有效的东西?
我能得到的最接近符合标准的代码来解决您的问题的是:
编写一个新的UTFConvert_to_sink
函数。
template<class T>
struct tag_t {};
template<class CharType, class Sink>
void UTFConvert_to_sink(std::basic_string_view<CharType> from, tag_t<CharType> to, Sink&& sink) {
for (CharType c : from)
sink(c);
}
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char> from, tag_t<std::char16_t> to, Sink&& sink); // TODO
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char> from, tag_t<std::char32_t> to, Sink&& sink); // TODO
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char16_t> from, tag_t<char> to, Sink&& sink); // TODO
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char16_t> from, tag_t<std::char32_t> to, Sink&& sink); // TODO
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char32_t> from, tag_t<char> to, Sink&& sink); // TODO
template<class Sink>
void UTFConvert_to_sink(std::basic_string_view<char32_t> from, tag_t<std::char16_t> to, Sink&& sink); // TODO
请注意,这些仅在Sink
上模板化。Sink
的工作原理应该从我的模板"相同到相同"中清楚。
UTFConvert
可以写在上面,如下所示:
template<class To, class From>
std::basic_string<To> UTFConvert( std::basic_string_view<From> from ) {
std::basic_string<To> retval;
UTFConvert_to_sink( from, tag_t<To>{}, [&retval]( To c ) {retval.push_back(c);} );
}
它处理有问题的每种类型。
现在剩下的就是wchar_t
UTFConvert_to_sink
.
using char_type_same_size_as_wchar_t = std::char16_t; // or char32_t depending on platform.
template<class From, class Sink>
void UTFConvert_to_sink(std::basic_string_view<From> from, tag_t<wchar_t> to, Sink&& sink) {
UTFConvert_to_sink( from, tag_t<char_type_same_size_as_wchar_t>{}, [&sink](auto c) {
wchar_t wc = c;
sink( wc );
});
}
我认为一切都在标准方面是可以的。 wchar_t函数应该编译为几乎一无所有。
如果您想从 -wchar_t
支持,事情确实会变得混乱,因为标准缺陷是无法在不调用new T[]
的情况下创建数组。 我们可以靠近我们清洗每个元素的地方。
template<class U, class T>
U* landry_pod( T* in ) {
static_assert( sizeof(T)==sizeof(U) );
static_assert( std::is_trivially_copyable<T>{} && std::is_trivially_copyable<U>{} );
char buff[sizeof(T)];
std::memcpy( buff, in, sizeof(T) );
U* r = ::new( (void*)in ) U;
std::memcpy( r, buff, sizeof(U) );
return r;
}
landry_pod<OutType>
是一个有趣的函数,因为它编译为零指令(试试看),但它是一种合法的方法,可以将指针转到 T 类型的可平凡可复制对象,并获取指向包含完全相同字节的相同大小 U 的平凡可复制对象的指针。
所以我能得到的最接近的是 运行basic_string_view<wchar_t>
,依次laundry_pod
每个元素,然后获取指针并用它们创建一个basic_string_view<char16_t>
,然后将它们提供给UTFConvert_to_sink
。
现在,所有这些都是荒谬的体操,围绕着标准中严格的混叠规则,它甚至不足以实际生成完全定义的行为。
请注意,我写了一个接受单个字符的sink
;也可以编写一个更高级的字符(即,需要与字符分开的长度,和/或允许您按顺序输入)。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- std::ofstream 作为类成员删除复制构造函数?
- 使用 memcpy() 复制到 std::chrono::milliseconds 会给出错误 -Werror=clas
- 在什么条件下使用 std::memcpy 在对象之间复制是安全的?
- 创建一个没有复制构造函数的类的 std::vector 的 std::vector
- 在 lambda 中锁定 std::shared_ptr 的复制操作
- std::p ackaged_task 应该删除带有 const 参数的复制 c'tor
- std::元组分配和复制/移动异常保证
- 转发复制的 std::tuple
- 使用std ::复制复制阵列时获取细分故障
- 将std ::复制转换为std :: memcpy不起作用
- STD ::复制在调试构建中失败
- 将指针而不是迭代器传递到std ::复制
- memcpy或std ::复制我的特定应用程序
- 我可以使用STD ::复制将数据的位模式从整数向量复制到一系列未签名的字符
- C++:std::复制失败,出现访问冲突读取位置错误
- 为什么memcpy无法复制特征矩阵数据,但std::复制成功
- std::复制钩子
- c++ std::复制结果不同于字符串构造函数
- C++11 std::复制内部函数