Поскольку разница между u8string
и string
заключается в том, что один шаблонизирован на char8_t
, а другой на char
, вопрос real заключается в том, в чем разница между использованием char8_t
на основе строк против char
на основе строк.
Это действительно сводится к следующему: кодирование на основе типов.
Любая строка на основе char
(char*
, char[]
, string
и т. Д.) Может быть закодирована в UTF-8. Но опять же, это может не . Вы можете разработать свой код, исходя из предположения, что каждый эквивалент char*
будет закодирован в UTF-8. И вы можете написать u8
перед каждым строковым литералом и / или иным образом убедиться, что они правильно закодированы. Но:
Код других людей может не совпадать. Таким образом, вы не можете использовать любую библиотеку, которая может вернуть char*
s, которые не используют кодировку UTF-8.
Вы можете случайно нарушить свои собственные заповеди. Ведь char not_utf8[] = "你好";
условно поддерживается C ++. Кодировка этого char[]
будет узкой кодировкой компилятора ... какой бы она не была . Это может быть UTF-8 на некоторых компиляторах и что-то еще на других.
Вы не можете сказать кодам других людей (или даже другим людям в вашей команде), что это то, что вы делаете. То есть ваш API не может объявить, что определенный char*
имеет кодировку UTF-8. Это должно быть что-то, что пользователь предполагает или прочел в вашей документации, а не то, что он видит в коде.
Обратите внимание, что ни одна из этих проблем не существует для пользователей UTF-16 или UTF-32. Если вы используете строку на основе char16_t
, все эти проблемы исчезнут. Если код других людей возвращает строку char16_t
, вы знаете, что они делают. Если они возвращают что-то еще, то вы знаете, что эти вещи, вероятно, не UTF-16. Ваш код на основе UTF-16 может взаимодействовать с их кодом. Если вы напишете API, который возвращает строку на основе char16_t
, то каждый, кто использует ваш код, может видеть по типу строки, что это за кодировка. И это гарантированно будет ошибкой компиляции: `char16_t not_utf16 [] =" 你好 ";
Теперь да, нет гарантии ни на одну из этих вещей. Любая конкретная строка char16_t
может иметь любые значения, даже те, которые недопустимы для UTF-16. Но char16_t
представляет тип, для которого допущением по умолчанию является конкретная кодировка. Учитывая, что если вы представите строку с этим типом, которая не закодирована в кодировке UTF-16, было бы разумным считать это ошибкой / вероломством со стороны пользователя, что это нарушение контракта.
Мы можем видеть, как на C ++ повлияло отсутствие аналогичных средств на основе типов для UTF-8. Рассмотрим filesystem::path
. Он может принимать строки в любой кодировке Unicode. Для UTF-16/32 конструктор path
принимает строки на основе char16/32_t
. Но вы не можете передать строку UTF-8 в конструктор path
; конструктор на основе char
предполагает, что кодирование является узким кодированием, определяемым реализацией, а не UTF-8. Поэтому вместо этого вы должны использовать filesystem::u8path
, которая является отдельной функцией, которая возвращает a path
, построенной из строки в кодировке UTF-8.
Что еще хуже, если вы попытаетесь передать строку в кодировке UTF-8, основанную на char
, в конструктор path
... он прекрасно скомпилируется. Несмотря на то, что в лучшем случае он не переносимый, он может просто работать.
char8_t
и все его атрибуты, такие как u8string
, существуют, чтобы предоставить пользователям UTF-8 ту же мощность, что и другие кодировки UTF. В C ++ 20 filesystem::path
получит перегрузки для строк на основе char8_t
, а u8path
устареет.
И, как дополнительный бонус, char8_t
не имеет специального языка псевдонимов. Таким образом, API, который принимает строки char8_t
, определенно API, который принимает массив символов, а не произвольный байтовый массив.