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

Oct 23 2008

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

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

Ответы

868 24revs,13users35%RogerPate Oct 23 2008 at 14:08

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

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

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

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зарезервированы
    • для соответствующих функций, которые работают с аргументами типа float и long double соответственно.
  • Имена, начинающиеся с SIGзаглавной буквы, зарезервированы.
    • для дополнительных имен сигналов.
  • Имена, начинающиеся с SIG_заглавной буквы, зарезервированы.
    • для дополнительных сигнальных воздействий.
  • Имена, начинающиеся с str, memили wcsза которыми следует строчная буква, зарезервированы.
    • для дополнительных функций строк и массивов.
  • Имена , начинающиеся с PRIили SCNпосле чего - либо строчной буквы или Xзарезервированы
    • для дополнительных макросов спецификатора формата
  • Имена, заканчивающиеся на _t, зарезервированы
    • для дополнительных имен типов.

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


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

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

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

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

202 paercebal Oct 23 2008 at 14:27

Правила, позволяющие избежать конфликта имен, содержатся как в стандарте C ++ (см. Книгу Страуструпа), так и упоминаются гуру C ++ (Саттер и т. Д.).

Личное правило

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

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

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

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

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

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

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

Выдержки из проекта C ++ 0x

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

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

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

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

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

Но также:

17.6.3.3.5 Определяемые пользователем буквальные суффиксы [usrlit.suffix]

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

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

40 RogerLipscombe Oct 23 2008 at 14:06

Из MSDN :

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

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

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

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

25 MaxLybbert Nov 15 2008 at 03:03

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

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

1 JohnMillikin Oct 23 2008 at 14:05

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

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