Читая разные C++-кодовые базы, можно заметить хаос в code style'ах. Qt использует к camelCase, стандартная библиотека и Boost используют snake_case, где-то любят PascalCase. Дополнительно хочу отметить нейминг приватных членов класса. Есть несколько часто встречающихся стилей:

_member // Подозрительно!
m_member
member_

Но у C++ в этом плане есть пара подводных мин. Некоторые имена зарезервированы стандартом для реализации: компилятора, стандартной библиотеки, рантайма и прочих приколов.

Что говорит стандарт

В разделе [lex.name] (пункты 4.1 — 4.2) черновика стандарта C++ указано, что часть идентификаторов зарезервирована для реализации и не должна использоваться пользовательским кодом. В частности, идентификаторы с двойным подчёркиванием __ где угодно, а также идентификаторы, начинающиеся с подчёркивания и заглавной буквы, зарезервированы для реализации для любого использования. Отдельно сказано, что идентификаторы, начинающиеся с подчёркивания, зарезервированы для реализации как имена в глобальном пространстве имён. Диагностика при этом не обязательна.

Если коротко:

class Widget {
private:
    int _size;       // обычно допустимо: член класса, _ + lowercase
    int _Toggle;     // плохо: _ + uppercase, зарезервировано
    int __cache;     // плохо: содержит __, зарезервировано
    int size_;       // обычно допустимо
    int size__x;     // плохо: содержит __, зарезервировано
};

int _global;         // плохо: имя в глобальном namespace, начинается с _

Значит ли это, что название переменной _size в классе запрещено?

Нет. И это как раз место, где легко ошибиться.

class Widget {
private:
    int _size;
};

Такое имя обычно не нарушает правило стандарта, потому что это не глобальное имя, но проблема в другом: стиль с leading underscore легко превращается в минное поле.

Сегодня мы назвали переменную:

int _size;

Завтра кто-то добавил акроним:

int _URL;

И всё: _ + uppercase — зарезервировано для реализации, ровно также как и данное имя:

int __cache;
// Или такое:
int cache__line;

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

Почему это может отстрелить вам ногу?

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

Raymond Chen в статье Microsoft DevBlogs отдельно показывает те же категории именований переменных. Он также отмечает, что популярная конвенция с подчёркиванием перед приватными членами легко ломается, если к имени переменной в начале добавить нижнее подчеркивание, а далее продолжить с заглавной буквы.

И компилятор не обязан вас спасать. Стандарт прямо говорит: no diagnostic required. То есть можно написать код, который формально нарушает правило, а компилятор спокойно промолчит.

Для проверки можно использовать clang-tidy с bugprone-reserved-identifier: он как раз ищет использование идентификаторов, зарезервированных для реализации, включая Name, глобальные name и __ внутри C++-идентификаторов.

А как делают большие проекты?

Google C++ Style Guide использует snake_case для переменных, а для data members классов — trailing underscore: table_name_. При этом поля struct называются как обычные переменные, без суффикса.

Chromium в целом следует Google C++ Style Guide, если в собственном гайде не указаны исключения.

LLVM использует CamelCase-подход: переменные должны быть существительными и начинаться с заглавной буквы, функции — с маленькой; для STL-подобных классов допускаются имена в стиле стандартной библиотеки.

C++ Core Guidelines не пытаются узаконить единственный стиль. Там прямо признаётся, что naming/layout часто субъективны, но всё равно предлагаются дефолтные правила: использовать единый стиль, предпочитать underscore_style, а ALL_CAPS оставлять только для макросов.

Boost рекомендует имена в нижнем регистре с подчёркиваниями, а макросы — в верхнем регистре с префиксом BOOST_.

Mozilla использует систему префиксов: m для member, s для static member, g для global, a для argument, k для constant; макросы начинаются с MOZ_.

Qt Creator использует m_ для members, но делает исключения для d и q pointers, связанных с d-pointer/pimpl-паттерном. Сам d-pointer в Qt используется для сокрытия деталей реализации и сохранения бинарной совместимости библиотек.

Что с _member в реальных проектах?

Такой стиль существует. Иногда он встречается даже в крупных и известных кодовых базах. Например, в Telegram Desktop можно встретить private members с leading underscore вроде lastUpdate, sets, _owner и так далее.

Заключение

В С++ полноподводных камней и нейминг переменных одно из них. Лично мне больше нравится STL или Boost‑like нейминг, то есть снейк кейс и придерживаюсь того, что нижнее подчеркивание и далее название переменной — плохая практика. А что думаете вы?

Комментарии (16)


  1. Explorus
    25.05.2026 10:11

    В каком стайлгайде Вы видели использование подчеркивания в начале имени? В конце имени подчеркиваине для того, чтобы показать, что это член класса (Google С++ Styleguide). Да, встречается код, где в начале имени подчеркивание добавляют из тех же соображений, но распространенной практикой это назвать сложно.


    1. shved4 Автор
      25.05.2026 10:11

      В распространенных стайл-гайдах (гугл, мозилла, ллвм) я такого не видел, но такой стиль достаточно распространен в разных закрытых конторках, галерках. Лично в моей организации есть соглашение о стиле кода, где такой нейминг приватных членов является "правильным" и рекомендуется к использованию, а остальные стили считаются неверными...


    1. shved4 Автор
      25.05.2026 10:11

      Такой стиль встречается в телеграм десктоп))


  1. slinkinone
    25.05.2026 10:11

    _ в начале имени члена класса весьма удобен - при написании и чтении когда сразу с первого символа названия переменной понимаешь что это не аргумент и не локальная переменная.


    1. Explorus
      25.05.2026 10:11

      Я бы очень хотел, чтобы кто-нибудь провел полноценное и непредвзятое исследование на тему удобного/быстрого восприятия/чтения этих самых подчеркиваний в именах. А до этих пор любые утверждения, что что-то удобно или неудобно являются исключительно субъективными. Как мне кажется, восприятие переменной происходит целиком (т.е. целым словом, мы не читаем по буквам), и мнение, что взгляд сперва целляется за подчеркивание выглядит несколько сомнительным. То есть таким же образом можно утверждать, что глаз цепляется за подчеркивание в конце имени после его прочтения и сразу понимаешь, что перед тобой член класса. В большинстве стайлгайдов префиксные подчеркивания запрещены из-за возможных случайных ошибок (тонкости стандарта порой даже мидлы не знают).


      1. slinkinone
        25.05.2026 10:11

        Так и есть - это субъективный-объективизм)

        Здесь нет правильно или неправильного ответа (пока это не ломает сборку компилятором) и определяется мэинтейнером проекта.


        1. shved4 Автор
          25.05.2026 10:11

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


      1. domix32
        25.05.2026 10:11

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

        Другая проблема - подсказки от редакторов. Когда у вас в коде всплывают var и var_, то член класса будет обычно отсортирован вторым по порядку и можно случайно выбрать не то что ожидалось и потом тратить время чтобы найти косяк в методе. _var в этом плане полезнее, т.к. сразу видно намерение.

        Что-нибудь типа

        Foo::Foo(int myvar)
          : myvar_(myvar) {} // поди не промахнись с выбором подсказки
        Foo::do_something(int myvar) {
          int data = process();
          // много строчек кода
          // 
          //
          myvar = data; // как скоро заметишь что myvar_ не поменялся
                        // без статанализа - только при запуске
        }
        
        Bar::Bar(int myvar)
          : _myvar(myvar) {} // при вводе подчерка покажет всех членов класса
        Bar::do_something(int myvar) {
          int data = process();
          // много строчек кода
          // 
          //
          _myvar = data; // переменная механически отлична от локальных переменных
        
        }

        что глаз цепляется за подчеркивание в конце имени после его прочтения и сразу понимаешь

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


  1. domix32
    25.05.2026 10:11

    Раньше использовали m_ и g_ для непубличных членов, сейчас перешёл на просто одинарное подчеркивание в членах класса. Двойное не использую, чтобы не зацепить никакие built in.


    1. monah_tuk
      25.05.2026 10:11

      Аналогично пробую. Ещё иногда пытаюсь в snake_case, но не хватает визуального отличия в некоторых местах в отсутствии подсветки. Но для себя писал стайл гайд, основа camelCase и m_


  1. sergio_nsk
    25.05.2026 10:11

    Был не прав.


  1. john_crew
    25.05.2026 10:11

    Кто как хочет, кому как удобно, и как договорятся, на мой взгляд.

    Глобальная функция / статичный метод класса - с заглавной буквы (GetInstance, Init).

    Метод класса, локальная переменная - с прописной (getValue, setOption, process).

    Константа или макрос - всё заглавными (MAX_SIZE, PI, APP_ID).

    Раньше я использовал _ вначале имен переменных/членов класса, мне было удобно в редакторе просматривать что у меня есть, но как-то незаметно для себя переехал на постфиксный стиль))


  1. oficsu
    25.05.2026 10:11

    Завтра кто-то добавил акроним: _URL

    Писать акроним капсом – это всё ещё плохая идея безотносительно подчёркивания, поскольку имя переменной капсом пересекается с общепринятым неймингом констант или макросов

    И возможно, что на практике такой нейминг в процессе ревью нанесёт даже больший ущерб, чем ifndr из-за подчёркивания


    1. shved4 Автор
      25.05.2026 10:11

      Я такое не раз встречал в коде старших разработчиков (5+ лет в индустрии)...


  1. yaroslavp
    25.05.2026 10:11

    m_<имя>, люблю такое, никаких подводных, всем все понятно


  1. old2ev
    25.05.2026 10:11

    В основном как в команде определено, так и нужно код оформлять, но лично для себя я использую следующие правила:

    • PascalCase - всё что является типом данных (class, struct, enum, и т.д.)

    • camelCase - всё что может вызываться (функции, функторы в том числе лямблы) и первое слово всегда глагол: set…, get…, is…, load… и т.д.

    • snake_case - данные (переменные, поля классов, аргументы функций), при чём не зависимо от константности.

    Как по мне венгерская нотация, в любом её проявлении(в т.ч. _*, __*, m_*, g_* и т.д.) давно изжила себя - на дворе далеко не 72-й год, для этих же целей в любой уважающей себя IDE давно присутствует такая замечательная штука как статический анализатор, который в случае необходимости подробно покажет и расскажет что, где и как хранится, зачем для этого городить какие-то избыточные правила нейминга, заставляя всю команду разработки их соблюдать. Тем более не вижу смысла в C++ использовать префиксы библиотек, как это делает Qt (QObject, QCoreApplication, QSettings и т.д.), для этих целей вообще-то в языке пространства имён есть.