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

931

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

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

Роджер Липскомб
источник
3
Страницу руководства glibc об этом можно найти по адресу gnu.org/software/libc/manual/html_node/Reserved-Names.html Редактировать: см. Также opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB
6
Просто отметим, что незнание этих правил не обязательно означает, что ваш код не будет компилироваться или выполняться, но вполне вероятно, что ваш код не будет переносимым на разные компиляторы и версию, поскольку нельзя гарантировать, что не будет имени столкновения Чтобы подкрепить это, я знаю об определенной реализации важной системы, которая повсеместно использовала в качестве соглашения об именах _ заглавную букву. Там, где нет ошибок из-за этого. Конечно, это плохая практика.
g24l

Ответы:

852

Правила (которые не изменились в 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 зарезервированные символы определяются здесь. Ограничения несколько более нюансированы, чем указанные выше.

Roger Pate
источник
14
Стандарт C ++ не «импортирует» стандарт C, не так ли? Насколько я знаю, они импортируют определенные заголовки, но не язык в целом или правила именования. Но да, этот тоже меня удивил. Но так как это C, он может применяться только к глобальным ns. Должно быть безопасно использовать _t внутри классов, как я это прочитал
jalf
27
Стандарт C ++ не "импортирует" Стандарт C. Он ссылается на стандарт Си. Введение в библиотеку C ++ гласит: «Библиотека также предоставляет возможности библиотеки Standard C». Это достигается путем включения заголовков библиотеки C Standard с соответствующими изменениями, но не путем «импорта». Стандарт C ++ имеет собственный набор правил, который описывает зарезервированные имена. Если имя, зарезервированное в C, должно быть зарезервировано в C ++, это место, чтобы сказать это. Но стандарт C ++ так не говорит. Так что я не верю, что вещи, зарезервированные в C, зарезервированы в C ++ - но я вполне могу ошибаться.
Йоханнес Шауб -
8
Вот что я обнаружил в проблеме «_t»: n1256 (C99 TC3) говорит: «Имена Typedef, начинающиеся с int или uint и заканчивающиеся _t», зарезервированы. Я думаю, что это все еще позволяет использовать имена типа "foo_t" - но я думаю, что они зарезервированы POSIX.
Йоханнес Шауб -
59
Итак, «допуск» зарезервирован POSIX, поскольку начинается с «to» + строчная буква? Бьюсь об заклад, много кода нарушает это правило!
Сьорд
23
@LokiAstari, « Стандарт C ++ определен в терминах стандарта C. По сути, он говорит, что C ++ - это C с этими различиями и дополнениями. » Чепуха! C ++ ссылается только на стандарт C в [basic.fundamental] и библиотеке. Если то, что вы говорите, правда, где C ++ говорит это _Boolи _Imaginaryне существует в C ++? Язык C ++ определяется явно, а не в терминах «правок» в C, в противном случае стандарт может быть намного короче!
Джонатан Уэйкли
198

Правила, позволяющие избежать столкновения имен, содержатся в стандарте 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]

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

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

paercebal
источник
9
@Meysam: __WRONG_AGAIN__содержит два последовательных подчеркивания (два в начале и два в конце), так что это неправильно в соответствии со стандартом.
paercebal
8
@ BЈовић: WRONG__WRONGсодержит два последовательных подчеркивания (два в середине), так что это неправильно в соответствии со стандартом
paercebal
2
размещение вашего кода в уникальном пространстве имен также помогает избежать коллизий : но этого все же недостаточно, поскольку идентификатор может конфликтовать с ключевым словом независимо от области действия (например, __attribute__для GCC).
Руслан
1
Почему существует проблема наличия двух последовательных подчеркиваний в середине в соответствии со стандартом? Пользовательские литеральные суффиксы применяются к литеральным значениям, таким как 1234567Lили 4.0f; IIRC это относится к ohttp: //en.cppreference.com/w/cpp/language/user_literal
Джейсон S
2
Why is there any problem of having two consecutive underscores in the middle according to the standard?Потому что стандарт говорит, что они зарезервированы. Это не совет о хорошем или плохом стиле. Это решение от стандарта. Почему они решили это? Я предполагаю, что первые компиляторы уже использовали такие соглашения неофициально до стандартизации.
paercebal
38

Из MSDN :

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

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

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

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

Роджер Липскомб
источник
2
Я нашел похожий текст в n3092.pdf (проект стандарта C ++ 0x) в разделе: «17.6.3.3.2 Глобальные имена»
paercebal
7
Интересно, что это, кажется, единственный ответ, который имеет прямой, краткий ответ на вопрос.
Hyde
9
@hyde: На самом деле это не так, так как оно пропускает правило, чтобы не иметь идентификаторов с ведущим подчеркиванием в глобальном пространстве имен. Смотрите ответ Роджера . Я бы очень осторожно относился к документам MS VC как к авторитету по стандарту C ++.
СБИ
@sbi Я имел в виду «вы можете использовать одно подчеркивание в качестве префикса переменной-члена, если за ним следует строчная буква» в этом ответе, который отвечает на вопрос по тексту вопроса прямо и кратко, без утопления в стене текста.
Хайд
5
Во-первых, я все еще считаю отсутствие какого-либо намека на то, что это правило не применяется к глобальному пространству имен, как сбой. Что еще хуже, так это то, что соседние символы подчеркивания запрещены не только в начале, но и в любом месте идентификатора. Таким образом, этот ответ не просто опускает факт, но фактически делает по крайней мере одно активно неправильное утверждение. Как я уже сказал, ссылаться на документы MSVC я бы не стал, если бы не вопрос исключительно о VC.
СБИ
25

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

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

Макс Либберт
источник
2

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

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

Джон Милликин
источник
3
Они распространены в библиотеках. Они не должны быть общими в коде пользователя.
Мартин Йорк,
43
Люди делают запись библиотеки в C, вы знаете.
Джон Милликин
7
«Да, подчеркивания могут использоваться где угодно в идентификаторе». Это неправильно для глобальных идентификаторов. Смотрите ответ Роджера .
sbi