Является ли поведение undefined # define/# undef идентификатором со специальным значением?
Ответ на вопрос Отключить проверку на переопределение в gcc, предложив использовать -Doverride=
в командной строке, чтобы отключить ошибки для ошибочного использования override, что фактически совпадает с добавлением:
#define override
в исходный файл.
Моя первоначальная реакция заключалась в том, что это похоже на поведение undefined, поскольку мы переопределяем ключевое слово, но смотрим на стандартный раздел С++ 11 2.12
Ключевые слова [lex.key] Я был удивлен, что ни переопределение, ни окончательный ключевые слова. Они описаны в предыдущем разделе 2.11
[lex.name], в котором говорится, что они являются идентификаторами со специальным значением:
Идентификаторы в таблице 3 имеют особое значение при появлении в определенный контекст [...]
а в таблице 3 обозначены идентификаторы со специальным значением и включают как переопределение, так и окончательное.
Вопрос в том, является ли поведение undefined переопределять (используя #define) идентификаторы со специальным значением? В этом отношении они рассматриваются иначе, чем ключевые слова?
Ответы
Ответ 1
Если вы используете стандартную библиотеку С++, поведение undefined переопределяет идентификаторы со специальным значением, это также относится к ключевым словам. Из проекта стандарта С++ 11 в разделе 17.6.4
[ограничения] мы имеем раздел 17.6.4.1
[constraints.overview], который гласит:
В этом разделе описываются ограничения на С++-программы, которые используют средства стандартной библиотеки С++ [...]
и под 17.6.4
у нас есть раздел 17.6.4.3.1
[macro.names], который гласит:
Единица перевода не должна содержать #define или #undef имена лексически идентичные ключевым словам, идентификаторам, перечисленным в таблице 3, или атрибут-токены, описанные в 7.6.
В таблице 3 перечислены идентификаторы со специальным значением. Мы можем видеть, что этот параграф также охватывает ключевые слова, и они обрабатываются одинаково.
Ответ 2
Стандартные файлы заголовков реализации разрешены для "реализации" стандартных функций с использованием макросов в случаях, когда макрос может удовлетворять требованиям к функции (включая обеспечение того, что аргументы оцениваются ровно один раз). Кроме того, таким макросам разрешено использовать ключевые слова или идентификаторы, поведение которых указано в стандарте или "зарезервировано для реализации"; использование таких макросов в контекстах, где ключевые слова или идентификаторы были переопределены, может иметь произвольные эффекты.
Было сказано, что историческая интерпретация этой формы UB будет заключаться в том, что компиляторы не должны уходить с дороги, чтобы вызвать дурацкое поведение, а за пределами "педантичных режимов" должен позволять коду пользователя присваивать значения зарезервированные идентификаторы, которые компилятор в противном случае не использовал бы. Это может быть полезно в случаях, когда код должен использоваться как для компиляторов, для которых требуется ключевое слово, например __packed
, так и компиляторы, которые не признают и не требуют такого ключевого слова.). Переопределение ключевых слов в моде, которую вы делаете, немного допище; это, вероятно, будет работать, но есть большая вероятность, что это нарушит поведение макроса стандартной библиотеки.