Является ли '\u0B95' литералом из нескольких символов?
В предыдущем ответе, который я дал, я ответил на следующее предупреждение, вызванное тем, что '\u0B95'
требует три байта и поэтому литерал с несколькими символами:
warning: multi-character character constant [-Wmultichar]
Но на самом деле, я не думаю, что я прав, и я не думаю, что gcc тоже. Стандарт гласит:
Обычный символьный литерал, содержащий более одного символа c-char, является литералом с несколькими символами.
Одно производственное правило для c-char - это имя универсального символа (т.е. \uXXXX
или же \UXXXXXXXX
). поскольку \u0B95
является одним c-char, это не литерал с несколькими символами. Но теперь это становится грязным. Стандарт также гласит:
Обычный символьный литерал, содержащий один символ c-char, имеет тип
char
со значением, равным числовому значению кодировки c-char в наборе символов выполнения.
Так что мой литерал имеет тип char
и значение символа в наборе символов выполнения (или значение, определяемое реализацией, если оно не существует в этом наборе). char
определяется как достаточно большой для хранения любого члена базового набора символов (который на самом деле не определен стандартом, но я предполагаю, что это означает базовый набор символов выполнения):
Объекты, объявленные как символы (char), должны быть достаточно большими для хранения любого члена базового набора символов реализации.
Следовательно, поскольку набор символов выполнения является надмножеством всех значений char
может держать, мой персонаж может не вписаться в char
,
Так какое значение имеет мой char
иметь? Кажется, это нигде не определено. Стандарт говорит, что для char16_t
литералы, если значение не представимо, программа некорректна. Хотя ничего не говорится об обычных литералах.
Так, что происходит? Это просто беспорядок в стандарте или я что-то упустил?
4 ответа
Я бы сказал следующее:
Значение символьного литерала определяется реализацией, если он выходит за пределы определенного реализацией диапазона, определенного для
char
(для литералов без префикса) ... (из раздела 2.14.3.4)
Если '\u0B95'
выходит за пределы заданного реализацией диапазона, определенного для char
(что было бы, если char
равен 8 битам), его значение определяется реализацией, после чего GCC может сделать свое значение последовательностью из нескольких c-char
с, таким образом, становясь многозначным буквальным.
Вы правы, согласно спецификации '\u0B95'
является символьным литералом с типом символа со значением, равным кодировке символа в наборе символов выполнения. И вы правы, что в спецификации ничего не сказано о случае, когда это невозможно для букв литералов из-за того, что один символ не может представлять это значение. Поведение не определено.
По этому вопросу в комитет поступило сообщение о дефекте: например, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html
Похоже, что в настоящее время предлагается указать, что эти символьные литералы также int
s и имеют определенные значения реализации (хотя предложенный язык не совсем подходит для этого), как и многоканальные литералы. Я не фанат этого решения, и я думаю, что лучшее решение - сказать, что такие литералы плохо сформированы.
Вот что реализовано в Clang: http://coliru.stacked-crooked.com/a/952ce7775dcf7472
Кто-то отправил ответ, который правильно ответил на вторую часть моего вопроса (какое значение char
есть?) но с тех пор удалил свой пост. Поскольку эта часть была правильной, я воспроизведу ее здесь вместе со своим ответом для первой части (это литерал с несколькими символами?).
'\u0B95'
не является литералом, состоящим из нескольких символов, и здесь gcc ошибается. Как указано в вопросе, многозначный литерал определяется (§2.14.3/1):
Обычный символьный литерал, содержащий более одного символа c-char, является литералом с несколькими символами.
Поскольку универсальное имя-символа является одним из расширений c-char, буквальное '\u0B95'
содержит только один c-char. Было бы разумно, если бы обычные литералы не могли содержать универсальное \u0B95
считаться шестью отдельными символами (\
, u
, 0
и т. д.), но я не могу найти это ограничение нигде. Следовательно, это один символ, а литерал не является литералом с несколькими символами.
Чтобы дополнительно поддержать это, почему это должно считаться несколькими символами? На данный момент мы даже не дали ему кодировку, поэтому мы не знаем, сколько байт это займет. В UTF-16 это займет 2 байта, в UTF-8 это займет 3 байта, а в некотором воображаемом кодировании это может занять всего 1 байт.
Так какое значение будет иметь символьный литерал? Сначала универсальное символьное имя сопоставляется с соответствующей кодировкой в наборе символов выполнения, если только оно не имеет отображения, и в этом случае оно имеет кодирование, определяемое реализацией (§2.14.3/5):
Универсальное имя символа преобразуется в кодировку в соответствующем наборе символов выполнения указанного символа. Если такой кодировки нет, универсальное символьное имя преобразуется в кодировку, определяемую реализацией.
В любом случае, char
литерал получает значение, равное числовому значению кодировки (§2.14.3/1):
Обычный символьный литерал, содержащий один символ c-char, имеет тип
char
со значением, равным числовому значению кодировки c-char в наборе символов выполнения.
Теперь важная часть, неудобно спрятанная в другом абзаце далее в разделе. Если значение не может быть представлено в char
, это получает определенное реализацией значение (§2.14.3/4):
Значение символьного литерала определяется реализацией, если оно выходит за пределы определенного реализацией диапазона, определенного для
char
(для литералов без префикса) ...
Поскольку у вас нет префикса кодировки символов, gcc (и любой другой совместимый компилятор) увидит '\u0B95'
и подумайте: 1) тип символа и 2) мультисимвол, потому что в строке более одного кода символа.
u'\u0B95'
является символом UTF16.u'\u0B95\u0B97'
является мультисимвольным символом UTF16.U'\ufacebeef'
является символом UTF32
и т.п.