Является ли С++ 20 'char8_t' таким же, как наш старый 'char'?

В справочнике по CPP документация,

Я заметил для char

Типы символов достаточно велики для представления любого восьмибитного UTF-8 блок кода (начиная с С++ 14)

и для char8_t

тип для представления символов UTF-8, должен быть достаточно большим представлять любую кодовую единицу UTF-8 (8 бит)

Означает ли это, что оба они одного типа? Или у char8_t есть какая-то другая особенность?

Ответ 1

char8_t отличается от char. Он ведет себя точно так же, как unsigned char, хотя в [basic.fundamental]/9

Тип char8_­t обозначает отдельный тип, базовый тип которого - unsigned char. Типы char16_­t и char32_­t обозначают различные типы, базовыми типами которых являются uint_­least16_­t и uint_­least32_­t, соответственно, в <cstdint>.

emphasis mine


Обратите внимание, что поскольку стандарт называет его отдельным типом, код, подобный

std::cout << std::is_same_v<unsigned char, char8_t>;

напечатает 0 (false), даже если char8_t реализован как unsigned char. Это потому, что это не псевдоним, а отдельный тип.


Следует также отметить, что char может быть реализован как signed char или unsigned char. Это означает, что char может иметь тот же диапазон и представление, что и char8_t, но они все еще являются отдельными типами. char, signed char, unsigned char и char8_t имеют одинаковый размер, но все они разных типов.

Ответ 2

Отказ от ответственности: я являюсь автором предложений char8_t P0482 и P1423.

В С++ 20 char8_t отличается от всех других типов. В соответствующем предложении для C, N2231 (который нуждается в обновлении и повторном предложении для WG14), char8_t будет typedef из unsigned char, аналогично существующим определениям типов для char16_t и char32_t.

В С++ 20 char8_t имеет базовое представление, которое соответствует unsigned char. Поэтому он имеет тот же размер (по крайней мере, 8-битный, но может быть больше), выравнивание и целочисленное преобразование ранга, что и unsigned char, но имеет другие правила наложения имен.

В частности, char8_t не был добавлен в список типов на [basic.lval] p11. [basic.life] p6.4, [basic.types] p2 или [basic.types] p4. Это означает, что, в отличие от unsigned char, его нельзя использовать для базового хранилища объектов другого типа, а также для изучения базового представления объектов других типов; другими словами, его нельзя использовать для псевдонимов других типов. Следствием этого является то, что объекты типа char8_t могут быть доступны через указатели на char или unsigned char, но указатели на char8_t не могут использоваться для доступа к данным char или unsigned char. Другими словами:

reinterpret_cast<const char   *>(u8"text"); // Ok.
reinterpret_cast<const char8_t*>("text");   // Undefined behavior.

Мотивация для отдельного типа с этими свойствами:

  1. Чтобы обеспечить отдельный тип для символьных данных UTF-8 по сравнению с символьными данными с кодировкой, которая либо зависит от локали, либо требует отдельной спецификации.

  2. Чтобы включить перегрузку для обычных строковых литералов по сравнению со строковыми литералами UTF-8 (поскольку они могут иметь разные кодировки).

  3. Чтобы гарантировать тип без знака для данных UTF-8 (независимо от того, подписан ли char или нет, определяется реализация).

  4. Чтобы обеспечить лучшую производительность с помощью сглаживания типа; оптимизаторы могут лучше оптимизировать типы, которые не имеют псевдонимов других типов.