Подтвердить что ты не робот

Это определенно незаконно ссылаться на зарезервированное имя?

В списке std-предложений был указан следующий код:

#include <vector>
#include <algorithm>

void foo(const std::vector<int> &v) {
#ifndef _ALGORITHM
  std::for_each(v.begin(), v.end(), [](int i){std::cout << i; }
#endif
}

Пусть игнорирует, для целей этого вопроса, почему этот код был дано и почему он был написан таким образом (так как была веская причина, но это не имело значения здесь). Он предполагает, что _ALGORITHM является защитой заголовка внутри стандартного заголовка <algorithm>, который поставляется с известной реализацией стандартной библиотеки. Здесь нет неотъемлемого намерения переносимости.

Теперь _ALGORITHM будет, конечно, зарезервированным именем, за:

[C++11: 2.11/3]: Кроме того, некоторые идентификаторы зарезервированы для использования реализацией С++ и стандартными библиотеками (17.6.4.3.2) и не должны использоваться иначе; диагностика не требуется.

[C++11: 17.6.4.3.2/1]: Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

  • Каждое имя, содержащее двойной знак подчеркивания _ _ или начинающийся с символа подчеркивания, за которым следует заглавная буква (2.12), зарезервировано для реализации для любого использования.
  • Каждое имя, начинающееся с символа подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.

У меня всегда создавалось впечатление, что цель этого отрывка состояла в том, чтобы помешать программистам определять/мутировать/не определять имена, которые подпадают под вышеуказанные критерии, так что разработчики стандартной библиотеки могут использовать такие имена, не опасаясь конфликтов с клиентом код.

Но в списке std-предложений было заявлено, что этот код сам плохо сформирован, просто ссылаясь на такое зарезервированное имя. Теперь я могу видеть, как использование фразы "не должно использоваться иначе" из [C++11: 2.11/3]: может действительно предположить, что.

Одно практическое обоснование заключалось в том, что макрос _ALGORITHM мог бы расширяться до некоторого кода, который стирает ваш жесткий диск, например. Однако, принимая во внимание вероятное намерение этого правила, я бы сказал, что такая вероятность имеет больше общего с очевидным, определяемым реализацией * характером названия _ALGORITHM, и меньше делать с ним прямое противозаконное обращение к нему.

* "определенная реализация" в своем смысле на английском языке, а не в стандартном смысле С++ фразы

Я бы сказал, что до тех пор, пока мы будем рады, что у нас будут определенные результаты, и мы должны исследовать, что этот макрос означает для нашей реализации (если он вообще существует!), он не должен по своей сути нелегально ссылаться на такой макрос, если мы не пытаемся его модифицировать.

Например, код, такой как следующий, используется повсюду, чтобы различать код, скомпилированный как C и код, скомпилированный как С++:

#ifdef __cplusplus
extern "C" {
#endif

и я никогда не слышал об этом жалобе.

Итак, что вы думаете? "Нельзя ли использовать иначе", просто написать такое имя? Или это, вероятно, не так строго (что может указывать на возможность настройки стандартной формулировки)?

4b9b3361

Ответ 1

Является ли это законным или нет специфичным для реализации (и специфичным для идентификатора).

Когда Стандарт дает реализацию, единственное право использовать эти имена, которое включает право сделать имена доступными в коде пользователя. Если реализация делает это, отлично.

Но если реализация явно не дает вам права, ясно, что "не должно использоваться иначе", что стандарт не имеет, и у вас есть поведение undefined.

Ответ 2

Важная часть зарезервирована для реализации. Это означает, что поставщик компилятора может использовать эти имена и даже документировать их. Затем ваш код может использовать эти имена как задокументированные. Это часто используется для расширений типа __builtin_expect, где поставщик компилятора избегает любого столкновения с вашими идентификаторами (объявленными вашим кодом), используя эти зарезервированные имена. Даже стандарт использует их для таких вещей, как __attribute__, чтобы убедиться, что он не нарушает существующий (юридический) код при добавлении новых функций.

Ответ 3

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1882

Каждый идентификатор, который содержит двойное сокращение __ или начинается с символа подчеркивания, за которым следует буква верхнего регистра, зарезервирован для реализации для любого использования.

любое использование. (аналогичный текст встречается как до, так и после этого исправления дефекта)

__cplusplus определяется стандартом. _ALGORITHM зарезервирован стандартом, который будет использоваться реализациями. Они кажутся совсем другими? (Два раздела стандарта конфликтуют, в этом говорится, что __cplusplus зарезервирован для любого использования, а другой использует его конкретно, но я думаю, что победитель этого конфликта ясен).

Идентификатор _ALGORITHM мог бы, по стандарту, использоваться как часть этапа предварительной обработки, чтобы сказать "заменить этот исходный код на код удаления жесткого диска". Его существование (до предварительной обработки или после) может быть достаточным, чтобы полностью изменить поведение вашей программы.

Теперь это маловероятно, но я не думаю, что это приводит к несоответствующей реализации. Это вопрос качества реализации.

Реализация может документировать и определять, что означает _ALGORITHM. Например, он может документировать, что это защита заголовка для <algorithm> и указывает, включен ли этот файл заголовка. Рассмотрение текущей реализации <algorithm>, поскольку документация, вероятно, будет далеко.

Я бы предположил, что использование __cplusplus в режиме C технически "так же плохо", как использование _ALGORITHM, но этот вопрос является вопрос, а не c. Я не вдавался в c стандарт для поиска котировок об этом.

Ответ 4

Имена в [cpp.predefined] отличаются. У них есть определенное значение, поэтому реализация не может зарезервировать их для использования, а использование их в программе имеет четко определенное переносное значение. Использование специфичного для реализации идентификатора, такого как пример _ALGORITHM, плохо сформировано, потому что оно нарушает правило.

Да, я полностью осведомлен о нескольких примерах, где спецификация библиотеки использует "должен" означать "это требование для кода пользователя, а нарушения - UB, а не плохо сформированы".

Относительно того, является ли он UB или определенным реализацией, запуск плохо сформированной программы приводит к UB. Стандартная формулировка ясно говорит о том, что программа плохо сформирована, UB возникает, если реализация по-прежнему решила принять программу и запустить ее.

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

Если пользователи обеспокоены такой плохой формой и потенциальным UB, и упомянутые пользователи хотят писать переносимый С++, они не должны использовать зарезервированные идентификаторы в портативных программах на С++. Если пользователи согласятся с тем, что независимо от стандарта, запрещающего такое использование, никакая практическая реализация не уничтожит ваш жесткий диск, они могут свободно использовать такие зарезервированные идентификаторы, но по букве стандарта такие использования все еще плохо сформированы.

Ответ 5

Исторически, целью использования таких маркеров "поведение w90" является то, что компиляторы могут приложить любое значение, которое они хотят для любого такого токена, которые не определены в стандарте C. Например, на некоторых встроенных процессорах использование __xdata в качестве класса хранения для переменной потребует, чтобы она хранилась в области ОЗУ, доступ к которой медленнее, чем обычная область с переменной памятью, но намного больше. На типичных процессорах этого семейства хранение для "нормальных" переменных будет ограничено примерно 100 байтами, но хранение для переменных xdata может быть намного больше - до 64 КБ. В стандарте практически ничего не говорится о том, какие компиляторы могут делать с такими директивами, хотя обычно (я не уверен, что стандарт отвечает за это поведение, хотя я не знаю, компиляторы его нарушают), такие токены обычно игнорируются в коде, который отключен с помощью #if или аналогичных директив.

Заголовочные файлы некоторых библиотек начинают свои собственные внутренние идентификаторы с чего-то, начинающегося с двух символов подчеркивания, но содержат шаблон, который вряд ли будет использоваться компилятором для каких-либо целей (например, версия 23 библиотеки Foozle может предшествовать ее идентификаторам с использованием __FZ23). Было бы совершенно законно, если бы будущие компиляторы использовали идентификаторы, начинающиеся с __FZ23, для других целей, и если бы это произошло, библиотеку Foozle нужно было бы изменить, чтобы использовать что-то еще. Если, однако, вполне вероятно, что значительное обновление компилятора, скорее всего, потребует перезаписывания библиотеки Foozle по другим причинам, этот риск может быть приемлемым по сравнению с риском идентификаторов, конфликтующих с внешним кодом.

Отметим также, что некоторые файлы заголовков проекта, предназначенные для процессора, для которого требуются директивы __, могут условно определять макросы с этими именами при компиляции для других процессоров, например:

#ifndef USE_XDATA
#define __XDATA
#endif

хотя несколько лучший шаблон обычно будет:

#ifdef USE_XDATA
#define XDATA __XDATA
#else
#define XDATA
#endif

При написании нового кода последний шаблон часто бывает лучше, но прежний шаблон иногда может быть полезен при адаптации существующего кода, написанного на платформе, который требует __XDATA, чтобы он мог использоваться как на платформах, которые используют/требуют, чтобы эта директива и на платформах, которые этого не делают.

Ответ 6

Независимо от того, является ли оно законным, это вопрос локального права. Значит ли это что-либо, и если да, то что, это вопрос определения языка. Когда вы используете имя, зарезервированное для реализации, поведение вашей программы undefined. Это означает, что определение языка не говорит вам, что делает программа. Ни больше ни меньше. Если в компиляторе вы используете документы, что делает конкретный зарезервированный идентификатор, вы можете использовать этот идентификатор с этим компилятором. Если вы охотитесь за заголовками и угадываете, что означают различные не документированные идентификаторы, вы можете их использовать, но не удивляйтесь, если ваш код сломается, когда последующее обновление что-то изменит.

Не зацикливайтесь на __cplusplus. Это основной язык, а материал о двойных подчеркиваниях и т.д. - это библиотека. Если это не убедительно, просто считайте это сбой. Вы можете использовать __cplusplus в программах на С++; его значение четко определено.