C/C++ UTF chaos

MSVC, кодировка исходников и строковые литералы

MSVC предоставляет аж 5 (прописью: пять) вариантов кодировки UTF для сохранения исходников:

Сохранение исходников любом UTF, кроме UTF-8, не кросс-платформенно и поэтому не рассматривается. Остаются UTF-8 с и без BOM.

В коде литералы строк могут быть “однобайтные” (char) и “широкие” (wchar_t):

const char*    cs =  "Я";
const wchar_t* ws = L"Я";

Символ 'Я' в UTF-8 представляется как D0 AF, а в UTF-16 – как 042F.

Литерал На диске В памяти
BOM Без BOM
char* D0 AF DF 00 D0 AF 00
wchar_t* 042F 0000 0420 0407 0000

В случае с BOM, в char* один символ DF – это 'Я' в Windows-1251, т. е. MSVC конвертирует строку в системную ANSI-кодировку (очевидно, с потерями). Зато с wchar_t* все в порядке. Без BOM наоборот, в char* записано желаемое UTF-8 представление строки, но вот в wchar_t* – непонятные 0420 0407. Это UTF-16 представление символов РЇ, которые в Windows-1251 имеют коды D0 AF (сравните с исходным UTF-8).

То есть, корректность обработки юникодных строковых литералов зависит от параметров сохранения файла, а одновременное использование char* и wchar_t* в этом случае и вовсе невозможно: