簡體   English   中英

將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);

charchar16_tchar32_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM