Каковы правила об использовании подчеркивания в идентификаторе С++?

В С++ часто называют имена переменных-членов с каким-то префиксом, чтобы обозначать тот факт, что они являются переменными-членами, а не локальными переменными или параметрами. Если вы пришли из фона MFC, вы, вероятно, используете m_foo. Иногда я видел myFoo.

С# (или, возможно, просто .NET), похоже, рекомендует использовать только подчеркивание, как в _foo. Это разрешено стандартом С++?

Ответ 1

Правила (которые не изменились в C++ 11):

  • Зарезервировано в любой области, в том числе для использования в качестве макросов реализации:
    • идентификаторы, начинающиеся со знака подчеркивания, за которыми сразу следует заглавная буква
    • идентификаторы, содержащие соседние подчеркивания (или "двойное подчеркивание")
  • Зарезервировано в глобальном пространстве имен:
    • идентификаторы, начинающиеся с подчеркивания
  • Кроме того, все в пространстве имен std зарезервировано. (Вы можете добавить шаблонные специализации, хотя.)

Из стандарта 2003 C++:

17.4.3.1.2 Глобальные имена [lib.global.names]

Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

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

165) Такие имена также зарезервированы в пространстве имен ::std (17.4.3.1).

Поскольку C++ основан на стандарте C (1.1/2, C++ 03), а C99 является нормативным справочным документом (1.2/1, C++ 03), они также применяются в стандарте C 1999 года:

7.1.3 Зарезервированные идентификаторы

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

  • Все идентификаторы, которые начинаются со знака подчеркивания, а также заглавной буквы или другого знака подчеркивания, всегда зарезервированы для любого использования.
  • Все идентификаторы, которые начинаются с подчеркивания, всегда зарезервированы для использования в качестве идентификаторов с областью действия файла как в обычном пространстве, так и в пространстве имен тега.
  • Каждое имя макроса в любом из следующих подпунктов (включая будущие направления библиотеки) зарезервировано для использования, как указано, если включен какой-либо из связанных с ним заголовков; если прямо не указано иное (см. 7.1.4).
  • Все идентификаторы с внешней связью в любом из следующих подпунктов (включая будущие направления библиотеки) всегда зарезервированы для использования в качестве идентификаторов с внешней связью. 154
  • Каждый идентификатор с областью файла, указанной в любом из следующих подпунктов (включая будущие направления библиотеки), зарезервирован для использования в качестве имени макроса и в качестве идентификатора с областью файла в том же пространстве имен, если включен какой-либо из связанных с ним заголовков.

Другие идентификаторы не зарезервированы. Если программа объявляет или определяет идентификатор в контексте, в котором она зарезервирована (за исключением случаев, разрешенных в 7.1.4), или определяет зарезервированный идентификатор как имя макроса, поведение не определено.

Если программа удаляет (с помощью #undef) любое макроопределение идентификатора в первой группе, указанной выше, поведение не определено.

154) Список зарезервированных идентификаторов с внешней связью включает в себя errno, math_errhandling, setjmp и va_end.

Другие ограничения могут применяться. Например, стандарт POSIX резервирует множество идентификаторов, которые могут отображаться в обычном коде:

  • Имена, начинающиеся с заглавной буквы E после цифры или заглавной буквы:
    • может использоваться для дополнительных имен кодов ошибок.
  • Имена, начинающиеся с is или to за которыми следует строчная буква
    • может использоваться для дополнительного тестирования символов и функций преобразования.
  • Имена, которые начинаются с LC_ за которым следует заглавная буква
    • может использоваться для дополнительных макросов, определяющих атрибуты локали.
  • Имена всех существующих математических функций с суффиксом f или l зарезервированы
    • для соответствующих функций, которые работают с плавающими и длинными двойными аргументами, соответственно.
  • Имена, которые начинаются с SIG за которым следует заглавная буква, зарезервированы
    • для дополнительных имен сигналов.
  • Имена, которые начинаются с SIG_ за которым следует заглавная буква, зарезервированы
    • для дополнительных сигнальных действий.
  • Имена, начинающиеся с str, mem или wcs за которыми следует строчная буква, зарезервированы
    • для дополнительных строковых и массивных функций.
  • Имена, начинающиеся с PRI или SCN за которыми следует любая строчная буква или X, зарезервированы
    • для дополнительных макросов спецификатора формата
  • Имена, заканчивающиеся на _t, зарезервированы
    • для дополнительных имен типов.

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


Лично я просто не начинаю идентификаторы с подчеркивания. Новое дополнение к моему правилу: нигде не используйте двойные подчеркивания, что легко, так как я редко использую подчеркивание.

После исследования этой статьи я больше не заканчиваю свои идентификаторы _t поскольку это зарезервировано стандартом POSIX.

Правило о любом идентификаторе, оканчивающемся на _t меня очень удивило. Я думаю, что это стандарт POSIX (пока не уверен), требующий пояснений, официальных главы и стиха. Это из руководства по GNU libtool, в котором перечислены зарезервированные имена.

CesarB предоставил следующую ссылку на зарезервированные символы POSIX 2004 и отмечает, что "там можно найти много других зарезервированных префиксов и суффиксов...". Зарезервированные символы POSIX 2008 определены здесь. Ограничения несколько более нюансированы, чем указанные выше.

Ответ 2

Правила, позволяющие избежать столкновения имен, находятся в стандарте С++ (см. книгу Stroustrup) и упомянуты гуру С++ (Sutter и т.д.).

Персональное правило

Поскольку я не хотел разбираться с делами и хотел простое правило, я разработал личный, который является простым и правильным:

При присвоении имени символу вы избегаете столкновения с библиотеками компилятора/ОС/стандарта, если вы:

  • никогда не начинайте символ с подчеркивания
  • никогда не называйте символ с двумя последовательными символами подчеркивания внутри.

Конечно, помещение вашего кода в уникальное пространство имен также помогает избежать столкновения (но не защитит от злых макросов)

Некоторые примеры

(Я использую макросы, потому что они больше загрязняют код символами C/С++, но это может быть что угодно: от имени переменной до имени класса)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

Выдержки из С++ 0x черновик

Из файла n3242.pdf (я ожидаю, что окончательный стандартный текст будет похож):

17.6.3.3.2 Глобальные имена [global.names]

Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

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

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

Но также:

17.6.3.3.5 Пользовательские суффиксы литерала [usrlit.suffix]

Литеральные идентификаторы суффиксов, которые не начинаются с подчеркивания, зарезервированы для будущей стандартизации.

Это последнее предложение сбивает с толку, если вы не считаете, что имя, начинающееся с одного подчеркивания и сопровождаемое строчной буквой, будет ОК, если not определено в глобальном пространстве имен...

Ответ 3

От MSDN:

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

Это означает, что вы можете использовать одно подчеркивание в качестве префикса переменной-члена, если оно следует за строчной буквой.

Это, очевидно, взято из раздела 17.4.3.1.2 стандарта С++, но я не могу найти исходный источник для полного стандартного онлайн.

См. также этот вопрос.

Ответ 4

Что касается другой части вопроса, то обычно стоит поставить знак подчеркивания в конце имени переменной, чтобы не столкнуться с чем-либо внутренним.

Я делаю это даже внутри классов и пространств имен, потому что мне нужно запомнить только одно правило (по сравнению с "в конце имени в глобальной области видимости и началом имени везде" ).

Ответ 5

Да, символы подчеркивания могут использоваться в любом месте идентификатора. Я считаю, что правила: любой из a-z, A-Z, _ в первом символе и те + 0-9 для следующих символов.

Префикс подчеркивания распространен в коде C - одно подчеркивание означает "private", а двойные символы подчеркивания обычно зарезервированы для использования компилятором.