Заметки про код-стайл в C++

от автора

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

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

Но у 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 отдельно показывает те же категории: name, Name, name в глобальной области и namepart в C++ попадают в опасную зону; он также отмечает, что популярная конвенция с подчёркиванием перед приватными членами легко ломается на имени вида _Toggle.

И самое неприятное: компилятор не обязан вас спасать. Стандарт прямо говорит: 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 нейминг, то есть снейк кейс и придерживаюсь того, что нижнее подчеркивание и далее название переменной — плохая практика. А что думаете вы?

ссылка на оригинал статьи https://habr.com/ru/articles/1038850/