Avatar ·

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

Всем привет! Я только начал изучать C++ и заметил, что в некоторых примерах кода люди используют подчёркивание в начале имён переменных, например `_value` или `__internal`. Я понимаю, что это, наверное, какой-то стиль именования, но мне кажется, что компилятор ведёт себя странно с такими именами. Например, я попробовал написать такой код: ```cpp #include class MyClass { private: int _value; public: MyClass(int v) : _value(v) {} int getValue() const { return _value; } }; int main() { MyClass obj(42); std::cout << obj.getValue() << std::endl; return 0; } ``` Всё скомпилировалось и работает, но потом я попробовал сделать так: ```cpp int _main() { return 0; } ``` И компилятор выдал предупреждение. А когда я попытался использовать `__LINE__` как имя переменной, компилятор вообще ругнулся ошибкой. Я в растерянности: какие вообще правила? Где можно, а где нельзя использовать подчёркивание? Почему некоторые имена с подчёркиванием работают, а другие нет? И что делать, если я хочу использовать подчёркивание для разделения слов в длинных именах, как в Python? Объясните, пожалуйста, подробно, как новичку. Хочется сразу писать правильно, чтобы потом не переписывать код из-за странных ошибок. <<>>

Основные правила стандарта C++ для подчёркиваний в идентификаторах

В C++ использование подчёркивания в именах строго регламентировано стандартом (начиная с C++03 и по C++23). Это сделано для того, чтобы избежать конфликтов с именами, зарезервированными для реализации компилятора, стандартной библиотеки и макросов препроцессора. Нарушение этих правил приводит к неопределённому поведению (undefined behavior), то есть программа может работать, а может и нет — компилятор не обязан выдавать ошибку.

Четыре главных правила (запомните их)

  • Правило 1: Идентификатор, содержащий два последовательных подчёркивания (__) в любом месте, всегда зарезервирован для реализации. Примеры: __myVar, my__var, myVar__.
  • Правило 2: Идентификатор, начинающийся с подчёркивания и следующей за ним заглавной буквы (_A, _B, _Z), зарезервирован для реализации. Пример: _MyClass.
  • Правило 3: Идентификатор, начинающийся с подчёркивания в глобальной области видимости (не внутри класса, функции или пространства имён), зарезервирован для реализации. Пример: _globalVar на уровне файла.
  • Правило 4: Все идентификаторы, начинающиеся с подчёркивания, в любом месте, зарезервированы для реализации в пространстве имён std. То есть нельзя объявлять свои имена вида std::_foo.

Когда можно использовать подчёркивание?

Внутри классов, функций или пространств имён (не в глобальной области) вы можете использовать подчёркивание в начале имени, если следующая буква — строчная (не заглавная). Это традиционный стиль для приватных членов класса (m_ или просто _). Например:

class MyClass {
private:
    int _value;      // OK: внутри класса, после _ идёт строчная буква
    int __value;     // ОШИБКА: два подчёркивания запрещены всегда
    int _Value;      // ОШИБКА: после _ идёт заглавная буква
public:
    int getValue() const { return _value; }
};

void myFunction() {
    int _localVar;   // OK: внутри функции, после _ строчная буква
}

Почему _main выдаёт предупреждение?

Имя _main начинается с подчёркивания и находится в глобальной области видимости (на уровне файла). Согласно правилу 3, это зарезервированное имя. Компилятор может выдать предупреждение (или ошибку), потому что такое имя может конфликтовать с внутренними символами реализации (например, в некоторых системах _main — это точка входа в программу на ассемблерном уровне). Аналогично, __LINE__ — это имя, начинающееся с двух подчёркиваний, что нарушает правило 1. К тому же __LINE__ — это предопределённый макрос препроцессора, его нельзя переопределять.

Как правильно использовать подчёркивание?

Для разделения слов в длинных идентификаторах (snake_case) используйте одиночное подчёркивание в середине имени, но никогда в начале (если это не приватный член класса со строчной буквой после подчёркивания). Примеры:

int my_variable_name;          // OK: snake_case
void process_user_input();     // OK
class data_processor { ... };  // OK

int _my_variable;              // OK только внутри функции/класса (строчная после _)
int my_variable_;              // OK, но не рекомендуется (trailing underscore)

Рекомендации по стилю (не обязательные, но общепринятые)

  • Не используйте подчёркивание в начале имени для обычных переменных и функций — это может случайно нарушить правила.
  • Для приватных членов класса часто используют m_ (например, m_value) или просто _ со строчной буквой — но это стиль, а не правило языка.
  • Для констант и макросов используйте ALL_CAPS (все заглавные с подчёркиваниями), но избегайте двойных подчёркиваний.
  • Никогда не начинайте идентификатор с подчёркивания в глобальной области — это почти гарантированно приведёт к неопределённому поведению в будущем.

Итог

Запомните три «красных флага»: два подчёркивания подряд, подчёркивание + заглавная буква, подчёркивание в глобальной области. Всё остальное (одиночное подчёркивание со строчной буквой внутри класса/функции) — допустимо, хотя и не всегда рекомендуется. Если сомневаетесь — просто используйте snake_case без начального подчёркивания.

<<>>

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

Правила кажутся избыточными, но у них есть практическая причина. Компиляторы и стандартная библиотека C++ активно используют имена с подчёркиванием для внутренних нужд. Например, _M_ в GCC или __ в Microsoft VC++. Если бы программист мог случайно создать переменную с именем _Lock (заглавная L), она могла бы пересечься с внутренним макросом библиотеки, что привело бы к трудноуловимым ошибкам.

Что делать, если очень хочется подчёркивание в начале?

Если вы пишете код только для себя и на одном компиляторе, вы можете использовать _value (строчная после _) внутри класса — это работает. Но при переносе кода на другой компилятор или при обновлении стандарта могут возникнуть проблемы. Лучший способ — использовать суффикс или префикс, который гарантированно не зарезервирован:

  • Префикс m_ (от member) — m_value
  • Суффикс _ — value_
  • Просто value без подчёркивания

Проверка на практике

Если вы пишете библиотеку, которая должна быть переносимой, используйте статический анализатор (например, clang-tidy с правилом readability-identifier-naming). Он предупредит о нарушении правил резервирования. Для учебных проектов достаточно запомнить: два подчёркивания и подчёркивание с заглавной — табу.