Рассмотрим эту безобидную программу на C ++:
#include <iostream>
int main() {
std::cout << "(Is this a trigraph??)" << std::endl;
return 0;
}
Когда я компилирую его, используя g ++ версии 5.4.0, я получаю следующую диагностику:
me@my-laptop:~/code/C++$ g++ -c test_trigraph.cpp
test_trigraph.cpp:4:36: warning: trigraph ??) ignored, use -trigraphs to enable [-Wtrigraphs]
std::cout << "(Is this a trigraph??)" << std::endl;
^
Программа запускается, и ее вывод соответствует ожидаемому:
(Is this a trigraph??)
Почему строковые литералы вообще разбираются на триграфы?
Другие компиляторы тоже делают это?
Триграфы были обработаны в фазе перевода 1 (однако они удалены в C ++ 17). Обработка строк, связанных с литералами, происходит на последующих этапах. Как указано в стандарте C ++ 14 (n4140) [Lex.phases] /1.1:
Приоритет среди синтаксических правил перевода определяется
следующие фазы.
- Физические символы исходного файла отображаются, в зависимости от реализации, в основной исходный набор символов
(введение символов новой строки для индикаторов конца строки), если
необходимо. Допустимый набор физических символов исходного файла:
реализации. Последовательности триграфа ([lex.trigraph])
заменены соответствующими односимвольными внутренними представлениями.
Любой символ исходного файла, не входящий в базовый набор символов
([lex.charset]) заменяется именем универсального символа, которое
обозначает этот характер. (Реализация может использовать любой внутренний
кодирование, пока фактический расширенный символ встречается в
исходный файл, и тот же расширенный символ, выраженный в исходном файле
файл как универсальное символьное имя (то есть, используя обозначение \ uXXXX),
обрабатываются аналогично, за исключением случаев, когда эта замена возвращается в
необработанный строковый литерал.)
Это произошло сначала, потому что, как вам сказали в комментариях, символы, которые обозначали триграфы, также должны были быть напечатаны.
Это поведение унаследовано от компиляторов C и в прежние времена, когда мы использовали последовательные терминалы, в которых использовалось только 7 бит (8-й был битом четности). Чтобы разрешить неанглийские языки со специальными символами (например, акцентированные символы на французском или на испанском), ИСО / МЭК 646 кодовые страницы использовали некоторый код ASCII (7 бит) для их представления. В частности, коды 0x23, 0x24 (#$
в ASCII) 0x40 (@
), От 0x5B до 0x5E ([\]^
), 0x60 (`) и от 0x7B до 0x7E ({|}~
) можно заменить на национальные варианты1.
Поскольку они имеют особое значение в C, их можно заменить в исходном коде на триграфы, используя только инвариантную часть ISO 646.
По соображениям совместимости, это было поддержано до C ++ 14, когда только динозавры все еще помнят о (не очень хороших) днях ISO646 и 7-битных кодовых страниц.
1 Например, используется французский вариант: 0x23 £
, 0x40 à
0x5b-0x5d °ç§
, 0x60 µ
0x7B-0x7E éùè¨