Я попробовал следующую печать символов 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 в этих стандартах?
cout
есть basic_ostream<char>
. Не могу представить, что это может работать с char8_t
.
@273K Хорошо. Это уважительная причина, но она не объясняет, почему код компилируется в соответствии со старыми стандартами и как печатать такие строки в C++20 и более поздних версиях.
комментарий в реализации cout #ifdef __cpp_char8_t // Эти удаленные перегрузки указаны в P1423.
Он не компилируется в C++20, потому что char8_t
был введен в C++20.
До 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.
Я думаю, что ваш обходной путь приводит к неопределенному поведению, потому что он пытается расширить пространство имен std без того, чтобы какой-либо из типов был программно-определяемым.
@heapunderrun Это расширяет пространство имен std
.
В чем причина запрета этого кода в С++ 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 обычно полезны для записи в файлы, но не очень удобны для записи в стандартный вывод.
std::wcout
работает со строками расширенных символовwchar_t
(литералы с префиксомL
), но не со строкамиchar8_t
,char16_t
иchar32_t
.