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

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

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

6 ответов

Решение

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

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


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

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

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

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

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

Из MSDN:

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

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

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

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

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

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

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

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

Во-первых, правила текущего рабочего проекта изложены в [lex.name] p3:

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

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

Более того, стандартная библиотека резервирует все имена, определенные вnamespace stdи несколько имен зомби; см. .

А как насчет POSIX?

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

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

ЛЮБОЙ Заголовок [резерв] Суффикс _t

- Стандарт POSIX 2008, 2.2.2.

В C++ почти всех проблем, связанных с POSIX, можно избежать с помощью пространств имен. Именно поэтому стандарт C++ может добавлять множество символов, таких какstd::enable_if_tбез нарушения совместимости с POSIX.

Визуализация

      int x;      // OK
int x_;     // OK
int _x;     // RESERVED
int x__;    // RESERVED (OK in C)
int __x;    // RESERVED
int _X;     // RESERVED
int assert; // RESERVED (macro name)
int x_t;    // RESERVED (only by POSIX)

namespace {
int y;      // OK
int y_;     // OK
int _y;     // OK
int y__;    // RESERVED (OK in C, ignoring namespaces)
int __y;    // RESERVED
int _Y;     // RESERVED
int assert; // RESERVED (macro name)
int y_t;    // OK
}

Вышеупомянутые правила применимы как к именованным, так и к неименованным пространствам имен. В любом случае, в следующем пространстве имен правила глобального пространства имен больше не применяются (см. [reserved.names.general][namespace.unnamed] ).

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

Несмотря на тоassertздесь не используется как макрос в стиле функции, имя зарезервировано. Именно поэтому предложение P2884 предполагает сделать это ключевым словом в C++26, и пока что это удалось .

Рекомендуемая практика

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

А что насчет самого по себе?

Некоторые люди используют_чтобы указать, что какая-то переменная или параметр функции не используется. Однако вы можете избежать этого с помощью:

      void foo(T _) { /* ... */ }
// replace with:
void foo(T) { /* ... */ }

std::scoped_lock _{mutex};
// replace with:
std::scoped_lock lock{mutex};

Вы также можете привести параметр кvoidнравиться(void)p, если речь идет о замалчивании предупреждений оpне используется, и вам нужна совместимость с C. См. раздел Зачем приводить неиспользуемые возвращаемые значения к void?.

Другие вопросы по тегам