![](/img/trans.png)
[英]how does one convert std::u16string -> std::wstring using <codecvt>?
[英]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);} );
}
處理所有有問題的類型。
現在剩下的是UTFConvert_to_sink
wchar_t
。
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函數應該編譯為幾乎沒有內容。
如果由於標准缺陷(如果不調用new T[]
就無法創建數組)想要支持from- wchar_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。
因此,我能得到的最接近的結果是依次laundry_pod
basic_string_view<wchar_t>
, laundry_pod
每個元素,然后獲取指針並與它們一起創建basic_string_view<char16_t>
,然后將其提供給UTFConvert_to_sink
。
現在, 所有這些都是荒謬的體操 ,無法解決標准中嚴格的別名規則,甚至還遠遠不足以實際產生完全定義的行為。
請注意,我寫了一個接收sink
,接收單個字符; 也可以編寫更高級的(即,將長度與字符分開,和/或允許您按順序輸入)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.