Является ли поведение 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, так и компиляторы, которые не признают и не требуют такого ключевого слова.). Переопределение ключевых слов в моде, которую вы делаете, немного допище; это, вероятно, будет работать, но есть большая вероятность, что это нарушит поведение макроса стандартной библиотеки.