Почему попытка напечатать строки в кодировке Unicode с помощью cout приводит к ошибке компиляции в новых стандартах C++?

Я попробовал следующую печать символов Unicode с помощью Visual C++ 2022 версии 17.4.4 с последним стандартом C++.

#include <iostream>

using namespace std;

int main()
{
  cout << u8"The official vowels in Danish are: a, e, i, o, u, \u00E6, \u00F8, \u00E5 and y.\n";
  return 0;
}

У меня ошибка компиляции:

1>C:\projects\cpp\test\test.cpp(7,8): error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char8_t *)': attempting to reference a deleted function
1>C:\projects\cpp\test\test.cpp(7,8): error C2088: '<<': illegal for class

Такое же поведение наблюдается со строковыми литералами u (utf-16) и U (utf-32).

Установка стандарта на C++17 или C++14 приводит к компиляции программы.

Каково обоснование запрета этого кода в стандартах C++ 20 и более поздних версий и как правильно печатать строковые литералы Unicode в этих стандартах?

std::wcout работает со строками расширенных символов wchar_t (литералы с префиксом L), но не со строками char8_t, char16_t и char32_t.
bobeff 01.02.2023 01:29
cout есть basic_ostream<char>. Не могу представить, что это может работать с char8_t.
273K 01.02.2023 01:31

@273K Хорошо. Это уважительная причина, но она не объясняет, почему код компилируется в соответствии со старыми стандартами и как печатать такие строки в C++20 и более поздних версиях.

bobeff 01.02.2023 01:34

комментарий в реализации cout #ifdef __cpp_char8_t // Эти удаленные перегрузки указаны в P1423.

pm100 01.02.2023 01:34

Он не компилируется в C++20, потому что char8_t был введен в C++20.

273K 01.02.2023 01:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
76
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

До C++20 u8"..." был const char[N]. Начиная с C++20, теперь const char8_t[N].

std::cout является std::basic_ostream<char> и поэтому не может выводить char8_t данные, начиная с C++20.

Возможный обходной путь:

std::basic_ostream<char>& operator<<(std::basic_ostream<char>& cout, const char8_t* s) {
  cout << reinterpret_cast<const char*>(s);
  return cout;
}

// Output: The official vowels in Danish are: a, e, i, o, u, æ, ø, å and y.

Хорошо. Я приму ваш ответ, если вы также предоставите решение о том, как напечатать такой литерал в С++ 20.

bobeff 01.02.2023 01:40

Я думаю, что ваш обходной путь приводит к неопределенному поведению, потому что он пытается расширить пространство имен std без того, чтобы какой-либо из типов был программно-определяемым.

heap underrun 01.02.2023 03:20

@heapunderrun Это расширяет пространство имен std.

273K 01.02.2023 03:22

В чем причина запрета этого кода в С++ 20?

Во-первых, до C++20 не было типа char8_t. Префикс u8 будет просто создавать данные char, влияя на их кодировку.

C++20 представил char8_t в p0482 и обратно несовместимо изменил префикс u8 для получения char8_t данных.

Но, как указывает p1423, это привело к молчаливому, контрпродуктивному изменению поведения, и предлагаемое решение состояло в том, чтобы вместо этого сделать операцию неправильно сформированной:

С принятием P0482R6 произошло непреднамеренное и незаметное изменение поведения. В C++17 следующий код записывает кодовые единицы литералов в стандартный вывод. В C++20 этот код теперь записывает символьный литерал в виде числа и адрес строкового литерала в стандартный вывод.

std::cout << u8'x';    // In C++20, writes the number 120.
std::cout << u8"text"; // In C++20, writes a memory address.

Это удивительное изменение, которое не приносит никакой пользы программистам. Добавление удаленных модулей вставки ostream позволило бы избежать этого неожиданного изменения поведения, сохранив при этом возможность указать поведение для этих операций в будущем (например, указать неявное перекодирование для кодирования выполнения).


как правильно печатать строковые литералы Unicode в этих стандартах?

Начиная с C++20, не существует стандартного способа печати char8_t, char16_t или char32_t напрямую в виде текста. Вам нужно будет преобразовать данные Unicode в собственную кодировку, используемую char или wchar_t, а затем распечатать ее. Однако не существует стандартного способа сделать такое преобразование (это не устарело).

Типы символов Unicode обычно полезны для записи в файлы, но не очень удобны для записи в стандартный вывод.

Другие вопросы по теме