Avatar ·

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

Всем привет! Я только начал изучать C++ и заметил, что в разных примерах кода символ подчеркивания `_` используется по-разному. Иногда он стоит в начале имени переменной, например `_value` или `__value`, иногда в конце — `value_`. А в некоторых стандартных библиотеках я видел имена вроде `_M_engine` или `__func__`. Я написал небольшой код, и он скомпилировался без ошибок: ```cpp #include int _myVar = 10; int main() { int __anotherVar = 20; std::cout << _myVar + __anotherVar; return 0; } ``` Но потом я прочитал, что некоторые идентификаторы с подчеркиванием зарезервированы для компилятора и стандартной библиотеки. Теперь я боюсь, что мой код может неожиданно сломаться или вызвать неопределенное поведение. Подскажите, пожалуйста, какие существуют правила использования подчеркивания в именах? Когда его можно использовать, а когда нельзя? И почему вообще эти правила такие строгие? Буду благодарен за подробное объяснение с примерами правильного и неправильного использования. <<>>

Основные правила стандарта C++

Стандарт C++ (раздел 17.4.3.1.2 в старых версиях, 6.1 в новых) четко определяет, какие идентификаторы с подчеркиванием зарезервированы. Нарушение этих правил приводит к неопределенному поведению (UB) — код может работать сегодня, но сломаться при смене компилятора или версии стандартной библиотеки.

Категорический запрет (никогда не используйте)

  • Двойное подчеркивание в любом месте: имена, содержащие __ (два подчеркивания подряд) — зарезервированы для реализации (компилятора и стандартной библиотеки). Примеры: __foo, bar__baz, __.
  • Подчеркивание + заглавная буква в начале: имена, начинающиеся с _ и следующей заглавной буквы — зарезервированы. Примеры: _Foo, _MyClass.

Ограниченное использование (только в определенных контекстах)

  • Подчеркивание в начале + строчная буква: зарезервировано в глобальной области видимости. В локальной области (внутри функции, класса) использовать можно, но это плохая практика. Пример: int _value; на глобальном уровне — нарушение; внутри функции — технически допустимо, но не рекомендуется.
  • Подчеркивание в конце: полностью разрешено в любом контексте. Это популярный способ избежать конфликтов с ключевыми словами или стандартными именами. Пример: class_, value_.

Почему такие строгие правила?

Компиляторы и стандартные библиотеки используют подчеркивания для внутренних нужд. Например, __cplusplus — макрос, _M_engine — внутреннее поле в GCC. Если ваш код случайно совпадет с таким именем, возникнет конфликт. Стандарт резервирует эти пространства имен, чтобы:

  • Обеспечить совместимость между разными компиляторами
  • Дать разработчикам компиляторов свободу в выборе внутренних имен
  • Защитить пользовательский код от случайных коллизий

Практические рекомендации (best practices)

// ❌ НЕПРАВИЛЬНО (зарезервировано)
int _Value = 5;       // подчеркивание + заглавная
int __value = 10;     // двойное подчеркивание
int _global = 20;     // подчеркивание + строчная в глобальной области

// ✅ ПРАВИЛЬНО (безопасно)
int value_ = 5;       // подчеркивание в конце
int my_value = 10;    // подчеркивание между словами
int _local = 20;      // ТОЛЬКО внутри функции/класса (но лучше избегать)

// ⚠️ ДОПУСТИМО, но не рекомендуется
void foo() {
    int _temp = 0;    // технически разрешено в локальной области
}

Исключения из правил

Некоторые имена с двойным подчеркиванием разрешены стандартом, если они определены в самом стандарте (например, __func__, __LINE__, __FILE__). Но их нельзя определять самостоятельно — их предоставляет компилятор.

Итог: безопасное правило — используйте подчеркивание только в середине или в конце имени. Никогда не начинайте имя с подчеркивания, если вы не пишете код внутри стандартной библиотеки или компилятора.

<<>>

Альтернативный взгляд: практика крупных проектов

Хотя стандарт допускает использование _ со строчной буквой в локальной области, многие крупные проекты (Google, LLVM, Qt) полностью запрещают любые идентификаторы, начинающиеся с подчеркивания. Причины:

  • Читаемость: подчеркивание в начале часто воспринимается как "внутренняя" или "приватная" переменная, что сбивает с толку.
  • Переносимость: код, который сегодня работает в локальной области, завтра может быть перемещен в глобальную при рефакторинге.
  • Единообразие: проще запомнить одно правило "не используй подчеркивание в начале" вместо трех.

Вместо этого используйте:

// Плохо: int _counter;
// Хорошо: int counter_;
// Или: int counter; (если нет конфликта)

Если вам нужно обозначить приватное поле класса, используйте m_ (Microsoft-style) или просто суффикс _ (стандарт C++ Core Guidelines). Например: int m_value; или int value_;.

Это упрощает код и делает его безопасным для любых компиляторов и версий C++.