Законно ли переопределять ключевое слово C ++?

В этой статье от Гуру недели говорится: It is illegal to #define a reserved word. Это правда? Я не могу найти ничего в норме, и, например, я уже видел, как программисты переопределяют новое.


person qdii    schedule 02.02.2012    source источник
comment
Совершенно возможно использовать #define для изменения значений зарезервированных слов. Фактически, он часто использовался в записях Международного конкурса запутанного кода C. Это возможно, потому что макросы, определенные #define, заменяются отдельной программой перед запуском реального компилятора C.   -  person Some programmer dude    schedule 02.02.2012
comment
уверены, что они не перегружали нового оператора? поскольку препроцессор запускается первым, проблема с # повторным определением ключевых слов заключается в том, что вы заменяете ключевые слова своей строкой замены, код, не ожидающий, что это, скорее всего, будет сломан после этого. Как правило, это плохая идея, зачем вам вообще это нужно?   -  person ted    schedule 02.02.2012
comment
@ted: идея состоит в том, чтобы переопределить новое ключевое слово в исходном файле, чтобы вызвать реализацию, зависящую от платформы: #define new newMac на Macintosh и #define new newPc на ПК. В других единицах трансляции соответствующие функции будут определять распределители памяти, зависящие от платформы. Я предполагаю, что идея заключалась в том, чтобы продолжать использовать new повсюду, имея поведение, зависящее от платформы, когда невозможно было переименовать каждый вызов в новый.   -  person qdii    schedule 02.02.2012
comment
Раньше мы исправляли некорректную область видимости переменных, объявленных в цикле for в Visual Studio 6, с помощью #define for if (false) else for   -  person Jack Aidley    schedule 04.03.2013
comment
Что вы имеете в виду, говоря, что я уже видел, как программисты переопределяют новое? Вы видели пример этого с использованием #define? Или вы имеете в виду перегрузку оператора new? Это две очень разные вещи.   -  person Code-Apprentice    schedule 25.06.2013
comment
@MonadNewb Я уже видел #define new newPS3. Индустрия видеоигр может немного походить на зоопарк.   -  person qdii    schedule 25.06.2013


Ответы (5)


17.4.3.1.1 Имена макросов [lib.macro.names]

1 Каждое имя, определенное как макрос в заголовке, зарезервировано для реализации для любого использования, если единица трансляции включает заголовок. 164)
2 Единица трансляции, которая включает заголовок, не должна содержать никаких макросов, которые определяют объявленные или определенные имена в этом заголовке. Такая единица перевода также не может определять макросы для имен, лексически идентичных ключевым словам.

Кстати, new - это оператор, и он может быть перегружен (заменен) пользователем, указав свою версию.

person Alok Save    schedule 02.02.2012
comment
Обратите внимание, что это правило применяется только к источникам, которые включают стандартный заголовок. Совершенно законно переопределить ключевое слово, если единица перевода не включает стандартный заголовок. (Однако это не сильно влияет на читаемость кода.) - person James Kanze; 02.02.2012
comment
Поэтому мне разрешено #define new, если моя единица перевода не содержит заголовков: P? @JamesKanze: почему стандартные заголовки? - person qdii; 02.02.2012
comment
@victor: стандартная (шаблонная) библиотека ?? - person ted; 02.02.2012
comment
@victor - язык позволяет компилятору знать, что находится в каждом стандартном заголовке, поэтому ему не нужно каждый раз перекомпилировать их. Поэтому вам запрещено изменять их, переопределяя некоторые слова в этих заголовках. Что делать со своими заголовками - решать вам! - person Bo Persson; 02.02.2012
comment
@BoPersson В данном случае это скорее стандарт, позволяющий реализации использовать обычный код C ++ в стандартных заголовках. Представляете, что могло бы случиться, если бы вы сделали #define while for до включения <algorithm>. Стандарт также требует, чтобы определенные функции были макросами, поэтому даже переопределение ключевого слова после включения заголовка может повлиять на ситуацию. - person James Kanze; 02.02.2012
comment
@ted: Я просто имел в виду тот факт, что этот фрагмент текста говорит о заголовках в более общем смысле, чем просто стандартные заголовки. Я хотел знать, почему Джеймс заявил, что это применимо только к источникам, которые включают стандартные заголовки. - person qdii; 02.02.2012
comment
В стандарте C ++ 03 термин header определен в 17.4.1.2 Headers [lib.headers] следующим образом: Элементы стандартной библиотеки C ++ объявлены или определены (при необходимости) в заголовке. Это не соответствует тому, что мы обычно считаем заголовками (файлы .h). В стандарте нет разницы между файлом .cpp и файлом .h; все они определены как исходные файлы. - person Joseph Mansfield; 02.02.2012
comment
Для C ++ 11 определение аналогично: каждый элемент стандартной библиотеки C ++ объявляется или определяется (при необходимости) в заголовке. На этот раз ясно, что каждый элемент может быть в разных заголовках. Однако в [macro.names] для ясности используется термин "заголовок стандартной библиотеки". - person Joseph Mansfield; 02.02.2012

Соответствующий раздел из C ++ 11:

17.6.4.3.1 Имена макросов [macro.names]

1 Единица перевода, которая включает заголовок стандартной библиотеки, не должна #define или #undef имен, объявленных в каком-либо заголовке стандартной библиотеки.
2 Единица перевода не должна #define или #undef имен, лексически идентичных ключевым словам.

Пункт 1 из C ++ 03 был удален. Второй абзац разделен на две части. В первой половине теперь конкретно указано, что она применяется только к стандартным заголовкам. Второй пункт был расширен и теперь включает любые единицы перевода, а не только те, которые включают заголовки.

Однако в Обзор для этого раздела стандарта (17.6.4.1 [constraints.overview]) указано:

В этом разделе описаны ограничения для программ C ++, использующих возможности стандартной библиотеки C ++.

Следовательно, если вы не используете стандартную библиотеку C ++, то можете делать то, что хотите.

Итак, чтобы ответить на ваш вопрос в контексте C ++ 11: вы не можете определить (или отменить определение) какие-либо имена, идентичные ключевым словам, в любой единице перевода, если вы используете стандартную библиотеку C ++.

person Joseph Mansfield    schedule 02.02.2012
comment
Каково определение использования стандартной библиотеки C ++? Например, использует ли программа int main() {} стандартную библиотеку C ++? Я спрашиваю об этом, потому что [basic.start.main] / 5 упоминает std::exit. - person L. F.; 09.11.2019

Они на самом деле ошибаются или, по крайней мере, не рассказывают об этом всей истории. Настоящая причина, по которой он запрещен, заключается в том, что он нарушает правило одного определения (которое, кстати, также упоминается как вторая причина, почему это незаконно).

Чтобы убедиться, что это действительно разрешено (переопределять ключевые слова), по крайней мере, если вы не используете стандартные библиотеки, вы должны взглянуть на совершенно другую часть стандарта, а именно на этапы перевода. В нем говорится, что входные данные только разлагаются на токены препроцессора перед предварительной обработкой и, глядя на них, нет различий между private и fubar, они оба identifiers для препроцессора. Позже, когда ввод раскладывается на token, замена уже произошла.

Было указано, что существует ограничение для программ, которые должны использовать стандартные библиотеки, но не очевидно, что пример, переопределяющий private, делает это (в отличие от фрагмента «Человек # 4: Языковой юрист», который использует его для вывод в cout).

В последнем примере упоминается, что этот трюк не попирается другими единицами перевода или не попирает другие. Имея это в виду, вам, вероятно, следует рассмотреть возможность того, что стандартная библиотека используется где-то еще, что приведет к действию этого ограничения.

person skyking    schedule 16.12.2015

Вот небольшая вещь, которую вы можете сделать, если не хотите, чтобы кто-то использовал goto. Просто оставьте следующее в его коде, где он этого не заметит.

#define goto { int x = *(int *)0; } goto

Теперь каждый раз, когда он пытается использовать оператор goto, его программа вылетает.

person Michael S. Miller    schedule 10.12.2012
comment
@JackAidley, да, но ты спрашивал себя, что в этом смешного? Ответ в том, что люди - садистские существа, и это пугает ... +1 за указание на человеческую природу. - person doc; 26.12.2014
comment
Это неопределенное поведение, и его лучше избегать. - person Abhijit; 18.04.2015
comment
Если вы думаете, что goto это плохо, вы не должны писать программы, если бы вы могли убедить меня, что нельзя использовать goto, я буду очень удивлен. - person Iharob Al Asimi; 18.04.2015
comment
@iharob Согласен. На ум приходят конечные автоматы как потенциально хорошее использование goto - person chbaker0; 19.04.2015
comment
Ваше утверждение, что это всегда приведет к сбою программы, не соответствует действительности. Следовательно, настоящий программист никогда не должен этого делать. На многих компиляторах этот оператор будет оптимизирован, и программа не выйдет из строя. Компилятор может легко оптимизировать все, что находится внутри оператора блока, потому что x в любом случае не используется. Даже если он не оптимизирован, это все равно неопределенное поведение, а не гарантированный сбой. - person Amy; 19.04.2015
comment
Если вы хотите наказать за использование goto, создание ненадежной ошибки времени выполнения - глупый способ сделать это. Если вы должны это сделать, сделайте это ошибкой времени компиляции: #define goto @ - person Keith Thompson; 19.04.2015
comment
@ chbaker0: Да, но goto не обязательно лучший способ реализовать конечный автомат; оператор switch в цикле делает текущее состояние явным. См. этот ответ для более подробного обсуждения того, когда подходит goto. - person Keith Thompson; 19.04.2015
comment
@Amy: Вы правы, он должен был написать: #define goto { int x = *(int volatile *)0; } goto. Тем не менее, он будет расширен как 2 оператора и вызовет синтаксические ошибки в if (xx) goto yy; else .... Он заслуживает большего количества голосов против за нанесение вреда языку. - person chqrlie; 19.04.2015

Насколько мне известно, это незаконно - ни один компилятор, с которым я еще не сталкивался, не выдаст ошибку, если вы это сделаете.

#define true false

#defining определенные ключевые слова могут вызывать ошибки при компиляции по другим причинам. Но многие из них просто приведут к очень странному поведению программы.

person Tom Tanner    schedule 02.02.2012
comment
Такие определения могут вызвать проблемы со стандартной библиотекой. - person James Kanze; 02.02.2012
comment
Возможно, по некоторым частям. У меня проблема в том, что, хотя это и незаконно, компилятор не обязан выдавать диагностику. И в зависимости от того, какие части стандартной библиотеки вы используете и где вы поместите этот #define в свой код, программа может нормально компилироваться. - person Tom Tanner; 02.02.2012
comment
Как и многие другие вещи, это неопределенное поведение. Компилятору не составит труда обнаружить это и вызвать ошибку времени компиляции, но я не знаю ни одной такой ошибки. - person James Kanze; 02.02.2012
comment
незаконно, но почти всегда игнорируется - person Tom Tanner; 05.03.2013