Перейти к основному содержанию
Перейти к основному содержанию

Руководство по стилю кода C++

Общие рекомендации

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

Форматирование

1. Большая часть форматирования выполняется автоматически с помощью clang-format.

2. Ширина отступа — 4 пробела. Настройте свою среду разработки так, чтобы нажатие клавиши Tab добавляло четыре пробела.

3. Открывающая и закрывающая фигурные скобки должны располагаться на отдельной строке.

inline void readBoolText(bool & x, ReadBuffer & buf)
{
    char tmp = '0';
    readChar(tmp, buf);
    x = tmp != '0';
}

4. Если всё тело функции — это один оператор (statement), его можно записать в одну строку. Ставьте пробелы вокруг фигурных скобок (помимо пробела в конце строки).

inline size_t mask() const                { return buf_size() - 1; }
inline size_t place(HashValue x) const    { return x & mask(); }

5. Для функций не ставьте пробелы вокруг скобок.

void reinsert(const Value & x)
memcpy(&buf[place_value], &x, sizeof(x));

6. В выражениях if, for, while и других перед открывающей скобкой ставится пробел (в отличие от вызовов функций).

for (size_t i = 0; i < rows; i += storage.index_granularity)

7. Добавляйте пробелы вокруг бинарных операторов (+, -, *, /, %, ...) и тернарного оператора ?:.

UInt16 year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0');
UInt8 month = (s[5] - '0') * 10 + (s[6] - '0');
UInt8 day = (s[8] - '0') * 10 + (s[9] - '0');

8. При вводе символа перевода строки перенесите оператор на новую строку и увеличьте перед ним отступ.

if (elapsed_ns)
    message << " ("
        << rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., "
        << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) ";

9. При необходимости вы можете использовать пробелы для выравнивания внутри строки.

dst.ClickLogID         = click.LogID;
dst.ClickEventID       = click.EventID;
dst.ClickGoodEvent     = click.GoodEvent;

10. Не ставьте пробелы вокруг операторов ., ->.

При необходимости оператор можно перенести на следующую строку. В этом случае отступ перед ним увеличивается.

11. Не используйте пробел между унарными операторами (--, ++, *, &, ...) и аргументом.

12. Ставьте пробел после запятой, но не перед ней. То же правило действует для точки с запятой внутри выражения for.

13. Не ставьте пробелы вокруг оператора [].

14. В выражении template <...> используйте пробел между template и <; не ставьте пробелы после < или перед >.

template <typename TKey, typename TValue>
struct AggregatedStatElement
{}

15. В классах и структурах располагайте public, private и protected на одном уровне с class/struct, а остальной код смещайте внутрь с отступом.

template <typename T>
class MultiVersion
{
public:
    /// Версия объекта для использования. shared_ptr управляет временем жизни версии.
    using Version = std::shared_ptr<const T>;
    ...
}

16. Если одно и то же namespace используется для всего файла и нет ничего иного примечательного, отступ внутри namespace не обязателен.

17. Если блок для выражения if, for, while или другого состоит из одного statement, фигурные скобки можно опустить. Вместо этого разместите statement на отдельной строке. Это правило также применяется к вложенным if, for, while, ...

Но если вложенный statement содержит фигурные скобки или else, внешний блок следует оформлять в фигурных скобках.

/// Завершить запись.
for (auto & stream : streams)
    stream.second->finalize();

18. В конце строк не должно быть пробелов.

19. Исходные файлы используют кодировку UTF-8.

20. В строковых литералах можно использовать символы, выходящие за пределы ASCII.

<< ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit.";

21. Не пишите несколько выражений в одной строке.

22. Группируйте блоки кода внутри функций и разделяйте их не более чем одной пустой строкой.

23. Разделяйте функции, классы и т. д. одной или двумя пустыми строками.

24. Модификатор const (относящийся к значению) должен записываться перед именем типа.

//корректно
const char * pos
const std::string & s
//некорректно
char const * pos

25. При объявлении указателя или ссылки знаки * и & должны быть отделены пробелами с двух сторон.

//верно
const char * pos
//неверно
const char* pos
const char *pos

26. При использовании шаблонных типов задавайте для них псевдонимы с помощью ключевого слова using (кроме самых простых случаев).

Иными словами, параметры шаблона задаются только в using и далее в коде не повторяются.

using может объявляться локально, например, внутри функции.

//корректно
using FileStreams = std::map<std::string, std::shared_ptr<Stream>>;
FileStreams streams;
//некорректно
std::map<std::string, std::shared_ptr<Stream>> streams;

27. Не объявляйте несколько переменных разных типов в одном объявлении.

//некорректно
int x, *y;

28. Не используйте приведение типов в стиле C.

//некорректно
std::cerr << (int)c <<; std::endl;
//корректно
std::cerr << static_cast<int>(c) << std::endl;

29. В классах и структурах группируйте члены данных и функции‑члены отдельно в рамках каждой области видимости.

30. Для небольших классов и структур нет необходимости отделять объявление метода от его реализации.

То же относится к небольшим методам в любых классах или структурах.

Для шаблонных классов и структур не отделяйте объявления методов от реализации (поскольку в противном случае они должны быть определены в одной и той же единице трансляции).

31. Вы можете переносить строки при достижении 140 символов вместо 80.

32. Всегда используйте префиксные операторы инкремента/декремента, если постфиксный вариант не требуется.

for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it)

Комментарии

1. Обязательно добавляйте комментарии ко всем нетривиальным фрагментам кода.

Это очень важно. Написание комментария может помочь вам понять, что этот участок кода не нужен или что он спроектирован неправильно.

/** Часть области памяти, которая может быть использована.
  * Например, если internal_buffer имеет размер 1 МБ, а из файла в буфер было загружено только 10 байт для чтения,
  * то working_buffer будет иметь размер только 10 байт
  * (working_buffer.end() будет указывать на позицию сразу после этих 10 доступных для чтения байт).
  */

2. Комментарии могут быть настолько подробными, насколько необходимо.

3. Размещайте комментарии перед кодом, который они описывают. В редких случаях комментарии могут располагаться после кода, на одной строке.

/** Разбирает и выполняет запрос.
*/
void executeQuery(
    ReadBuffer & istr, /// Откуда читать запрос (и данные для INSERT, если применимо)
    WriteBuffer & ostr, /// Куда записывать результат
    Context & context, /// БД, таблицы, типы данных, движки, функции, агрегатные функции...
    BlockInputStreamPtr & query_plan, /// Здесь может быть записано описание выполнения запроса
    QueryProcessingStage::Enum stage = QueryProcessingStage::Complete /// До какой стадии обрабатывать SELECT-запрос
    )

4. Комментарии должны быть написаны только на английском языке.

5. Если вы пишете библиотеку, добавляйте подробные комментарии, объясняющие её, в основной заголовочный файл.

6. Не добавляйте комментарии, которые не несут дополнительной информации. В частности, не оставляйте пустые комментарии вроде этого:

/*
* Имя процедуры:
* Исходное имя процедуры:
* Автор:
* Дата создания:
* Даты модификации:
* Авторы модификаций:
* Исходное имя файла:
* Назначение:
* Назначение:
* Обозначение:
* Используемые классы:
* Константы:
* Локальные переменные:
* Параметры:
* Дата создания:
* Назначение:
*/

Пример заимствован с сайта http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/.

7. Не пишите мусорные комментарии (автор, дата создания и т. д.) в начале каждого файла.

8. Однострочные комментарии начинаются с трёх слэшей: ///, а многострочные — с /**. Такие комментарии считаются «документацией».

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

9. Многострочные комментарии не должны иметь пустых строк в начале и в конце (кроме строки, которая закрывает многострочный комментарий).

10. Для закомментирования кода используйте обычные комментарии, а не «документирующие» комментарии.

11. Удаляйте закомментированные части кода перед коммитом.

12. Не используйте ненормативную лексику в комментариях или коде.

13. Не пишите ЗАГЛАВНЫМИ БУКВАМИ. Не используйте избыточную пунктуацию.

/// ЧТО ЗА ОШИБКА???

14. Не используйте комментарии в качестве разделителей.

///******************************************************

15. Не начинайте обсуждения в комментариях.

/// Зачем вы это сделали?

16. Нет необходимости писать в конце блока комментарий, поясняющий, чему он посвящён.

/// for

Имена

1. Используйте строчные буквы и символ нижнего подчеркивания в именах переменных и членов классов.

size_t max_block_size;

2. Для имен функций (методов) используйте стиль camelCase, начиная с строчной буквы.

std::string getName() const override { return "Memory"; }

3. Для имен классов и структур используйте CamelCase, начиная с заглавной буквы. Для интерфейсов не используются префиксы, кроме I.

class StorageMemory : public IStorage

4. Объявления using именуются так же, как классы.

5. Имена аргументов типовых параметров: в простых случаях используйте T; T, U; T1, T2.

В более сложных случаях либо следуйте правилам для имен классов, либо добавляйте префикс T.

template <typename TKey, typename TValue>
struct AggregatedStatElement

6. Имена константных аргументов шаблонов: либо следуют правилам для имён переменных, либо в простых случаях используют N.

template <bool without_www>
struct ExtractDomain

7. Для абстрактных классов (интерфейсов) можно добавлять префикс I.

class IProcessor

8. Если вы используете переменную локально, можно использовать краткое имя.

Во всех остальных случаях используйте имя, отражающее назначение.

bool info_successfully_loaded = false;

9. Имена define-ов и глобальных констант записываются в формате ALL_CAPS с подчёркиваниями.

#define MAX_SRC_TABLE_NAMES_TO_STORE 1000

10. Имена файлов должны соответствовать стилю их содержимого.

Если файл содержит единственный класс, назовите файл так же, как класс (CamelCase).

Если файл содержит единственную функцию, назовите файл так же, как функцию (camelCase).

11. Если имя содержит аббревиатуру, то:

  • Для имен переменных аббревиатура должна быть записана строчными буквами: mysql_connection (а не mySQL_connection).
  • Для имен классов и функций сохраняйте заглавные буквы в аббревиатуре: MySQLConnection (а не MySqlConnection).

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

FileQueueProcessor(
    const std::string & path_,
    const std::string & prefix_,
    std::shared_ptr<FileHandler> handler_)
    : path(path_),
    prefix(prefix_),
    handler(handler_),
    log(&Logger::get("FileQueueProcessor"))
{
}

Суффикс с символом нижнего подчеркивания можно опустить, если аргумент не используется в теле конструктора.

13. Не должно быть различий в именовании локальных переменных и членов класса (префиксы не требуются).

timer (а не m_timer)

14. Для констант в enum используйте CamelCase, начиная с заглавной буквы. Допустим также вариант ALL_CAPS. Если enum является нелокальным, используйте enum class.

enum class CompressionMethod
{
    QuickLZ = 0,
    LZ4     = 1,
};

15. Все имена должны быть на английском языке. Транслитерация слов на иврите не допускается.

Не так: T_PAAMAYIM_NEKUDOTAYIM

16. Допускаются сокращения, если они хорошо известны (когда вы можете легко найти расшифровку сокращения в Википедии или через поисковик).

AST, SQL.

Не так: NVDH (какие-то случайные буквы)

Неполные слова допустимы, если сокращённая форма общеупотребительна.

Вы также можете использовать сокращение, если его полное название приведено рядом с ним в комментариях.

17. Имена файлов с исходным кодом C++ должны иметь расширение .cpp. Заголовочные файлы должны иметь расширение .h.

Как писать код

1. Управление памятью.

Ручное освобождение памяти (delete) допускается только в библиотечном коде.

В библиотечном коде оператор delete можно использовать только в деструкторах.

В прикладном коде память должна освобождаться объектом, который ей владеет.

Примеры:

  • Проще всего разместить объект на стеке или сделать его полем другого класса.
  • Для большого числа небольших объектов используйте контейнеры.
  • Для автоматического освобождения небольшого числа объектов, размещённых в куче, используйте shared_ptr/unique_ptr.

2. Управление ресурсами.

Используйте RAII и см. выше.

3. Обработка ошибок.

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

В офлайновых приложениях для обработки данных часто допустимо не перехватывать исключения.

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

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

/// Если вычислений ещё не было, вычислить первый блок синхронно
if (!started)
{
    calculate();
    started = true;
}
else /// Если вычисления уже выполняются, ожидать результата
    pool.wait();

if (exception)
    exception->rethrow();

Никогда не подавляйте исключения, не обрабатывая их. Никогда не записывайте в лог все исключения без разбора.

//Неверно
catch (...) {}

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

catch (const DB::Exception & e)
{
    if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION)
        return nullptr;
    else
        throw;
}

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

if (0 != close(fd))
    throw ErrnoException(ErrorCodes::CANNOT_CLOSE_FILE, "Не удалось закрыть файл {}", file_name);

Вы можете использовать assert для проверки инвариантов в коде.

4. Типы исключений.

Нет необходимости использовать сложную иерархию исключений в прикладном коде. Текст исключения должен быть понятен системному администратору.

5. Выбрасывание исключений из деструкторов.

Это не рекомендуется, но допускается.

Используйте следующие подходы:

  • Создайте функцию (done() или finalize()), которая заранее выполнит всю работу, способную привести к возникновению исключения. Если эта функция была вызвана, в деструкторе впоследствии не должно возникать исключений.
  • Задачи, которые слишком сложны (например, отправка сообщений по сети), можно вынести в отдельный метод, который пользователь класса должен будет вызвать до разрушения объекта.
  • Если в деструкторе возникает исключение, лучше записать его в лог, чем скрывать (если логгер доступен).
  • В простых приложениях допустимо полагаться на std::terminate (в случаях noexcept по умолчанию в C++11) для завершения работы при возникновении исключений.

6. Анонимные блоки кода.

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

Block block = data.in->read();

{
    std::lock_guard<std::mutex> lock(mutex);
    data.ready = true;
    data.block = block;
}

ready_any.set();

7. Многопоточность.

В программах для офлайн-обработки данных:

  • Старайтесь добиться максимально возможной производительности на одном ядре CPU. При необходимости затем можно распараллелить код.

В серверных приложениях:

  • Используйте пул потоков для обработки запросов. На данный момент у нас не было задач, требующих переключения контекста в пространстве пользователя.

fork не используется для распараллеливания.

8. Синхронизация потоков.

Часто можно организовать работу так, чтобы разные потоки использовали разные ячейки памяти (ещё лучше — разные кэш-линии) и не требовали синхронизации (кроме joinAll).

Если синхронизация необходима, в большинстве случаев достаточно использовать мьютекс с lock_guard.

В остальных случаях используйте системные примитивы синхронизации. Не используйте активное ожидание (busy wait).

Атомарные операции следует использовать только в самых простых случаях.

Не пытайтесь реализовывать неблокирующие структуры данных, если это не ваша основная область экспертизы.

9. Указатели против ссылок.

В большинстве случаев предпочитайте ссылки.

10. const.

Используйте константные ссылки, указатели на константы, const_iterator и const-методы.

Считайте const значением по умолчанию и используйте не-const только при необходимости.

При передаче переменных по значению использование const обычно не имеет смысла.

11. unsigned.

Используйте unsigned, если это необходимо.

12. Числовые типы.

Используйте типы UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32 и Int64, а также size_t, ssize_t и ptrdiff_t.

Не используйте для чисел следующие типы: signed/unsigned long, long long, short, signed/unsigned char, char.

13. Передача аргументов.

Передавайте сложные объекты по значению, если они затем будут перемещены, и используйте std::move; передавайте по ссылке, если нужно обновлять значение в цикле.

Если функция принимает на себя владение объектом, созданным в куче, сделайте тип аргумента shared_ptr или unique_ptr.

14. Возвращаемые значения.

В большинстве случаев просто используйте return. Не пишите return std::move(res).

Если функция выделяет объект в куче и возвращает его, используйте shared_ptr или unique_ptr.

В редких случаях (обновление значения в цикле) может потребоваться вернуть значение через аргумент. В этом случае аргумент должен быть ссылкой.

using AggregateFunctionPtr = std::shared_ptr<IAggregateFunction>;

/** Позволяет создавать агрегатную функцию по её имени.
  */
class AggregateFunctionFactory
{
public:
    AggregateFunctionFactory();
    AggregateFunctionPtr get(const String & name, const DataTypes & argument_types) const;

15. namespace.

Нет необходимости использовать отдельный namespace для кода приложения.

Небольшим библиотекам это тоже не требуется.

Для средних и крупных библиотек поместите всё в один namespace.

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

В .cpp-файле вы можете использовать static или анонимный namespace, чтобы скрыть символы.

Также namespace можно использовать для enum, чтобы соответствующие имена не попадали во внешний namespace (но лучше использовать enum class).

16. Отложенная инициализация.

Если для инициализации требуются аргументы, обычно не следует писать конструктор по умолчанию.

Если позже вам понадобится отложить инициализацию, вы можете добавить конструктор по умолчанию, который будет создавать невалидный объект. Или, для небольшого числа объектов, вы можете использовать shared_ptr/unique_ptr.

Loader(DB::Connection * connection_, const std::string & query, size_t max_block_size_);

/// Для отложенной инициализации
Loader() {}

17. Виртуальные функции.

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

18. Кодировки.

Используйте UTF-8 везде. Используйте std::string и char *. Не используйте std::wstring и wchar_t.

19. Логирование.

Смотрите примеры в коде.

Перед коммитом удаляйте все бессмысленные и отладочные логи, а также любые другие виды отладочного вывода.

Логирования в циклах следует избегать, даже на уровне Trace.

Логи должны быть читаемыми на любом уровне логирования.

В основном используйте логирование только в прикладном коде.

Сообщения логов должны быть написаны на английском языке.

Желательно, чтобы лог был понятен системному администратору.

Не используйте ненормативную лексику в логе.

Используйте в логе кодировку UTF-8. В редких случаях можно использовать в логе не-ASCII-символы.

20. Ввод-вывод.

Не используйте iostreams во внутренних циклах, критичных для производительности приложения (и никогда не используйте stringstream).

Вместо этого используйте библиотеку DB/IO.

21. Дата и время.

См. библиотеку DateLUT.

22. include.

Всегда используйте #pragma once вместо include guards.

23. using.

using namespace не используется. Можно использовать using с чем-то конкретным. Но делайте это локально внутри класса или функции.

24. Не используйте trailing return type для функций без необходимости.

auto f() -> void

25. Объявление и инициализация переменных.

//правильный способ
std::string s = "Hello";
std::string s{"Hello"};

//неправильный способ
auto s = std::string{"Hello"};

26. Для виртуальных функций пишите virtual в базовом классе, а в производных классах пишите override вместо virtual.

Неиспользуемые возможности C++

1. Виртуальное наследование не используется.

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

// Традиционный способ без синтаксического сахара
template <typename G, typename = std::enable_if_t<std::is_same<G, F>::value, void>> // SFINAE через std::enable_if, использование ::value
std::pair<int, int> func(const E<G> & e) // явно указанный возвращаемый тип
{
    if (elements.count(e)) // проверка принадлежности через .count()
    {
        // ...
    }

    elements.erase(
        std::remove_if(
            elements.begin(), elements.end(),
            [&](const auto x){
                return x == 1;
            }),
        elements.end()); // идиома remove-erase

    return std::make_pair(1, 2); // создание пары через make_pair()
}

// С синтаксическим сахаром (C++14/17/20)
template <typename G>
requires std::same_v<G, F> // SFINAE через концепт C++20, использование псевдонима шаблона C++14
auto func(const E<G> & e) // автовыведение возвращаемого типа (C++14)
{
    if (elements.contains(e)) // проверка принадлежности через .contains в C++20
    {
        // ...
    }

    elements.erase_if(
        elements,
        [&](const auto x){
            return x == 1;
        }); // std::erase_if из C++20

    return {1, 2}; // или: return std::pair(1, 2); // создание пары через список инициализации или прямую инициализацию (C++17)
}

Платформа

1. Мы пишем код под конкретную платформу.

Но при прочих равных предпочтителен кроссплатформенный или переносимый код.

2. Язык: C++20 (см. список доступных возможностей C++20).

3. Компилятор: clang. На момент написания (март 2025 года) код компилируется с помощью clang версии не ниже 19.

Используется стандартная библиотека (libc++).

4. ОС: Ubuntu Linux, не старше Precise.

5. Код пишется для архитектуры процессора x86_64.

Набор инструкций процессора — минимальный поддерживаемый среди наших серверов. В данный момент это SSE 4.2.

6. Используйте флаги компиляции -Wall -Wextra -Werror -Weverything с некоторыми исключениями.

7. Используйте статическую компоновку со всеми библиотеками, кроме тех, которые сложно подключать статически (см. вывод команды ldd).

8. Код разрабатывается и отлаживается с релизными настройками.

Инструменты

1. KDevelop — хорошая среда разработки (IDE).

2. Для отладки используйте gdb, valgrind (memcheck), strace, -fsanitize=... или tcmalloc_minimal_debug.

3. Для профилирования используйте Linux Perf, valgrind (callgrind) или strace -cf.

4. Исходный код хранится в Git.

5. Сборка выполняется с помощью CMake.

6. Программы выпускаются в виде deb-пакетов.

7. Коммиты в master не должны ломать сборку.

Хотя рабочими считаются только отдельные ревизии.

8. Делайте коммиты как можно чаще, даже если код готов лишь частично.

Для этого используйте ветки.

Если ваш код в ветке master пока не собирается, исключите его из сборки перед push. Вам нужно будет доделать его или удалить в течение нескольких дней.

9. Для нетривиальных изменений используйте ветки и публикуйте их на сервере.

10. Неиспользуемый код удаляется из репозитория.

Библиотеки

1. Используется стандартная библиотека C++20 (допускаются экспериментальные расширения), а также фреймворки boost и Poco.

2. Не допускается использование библиотек из пакетов ОС. Также не допускается использование предустановленных в системе библиотек. Все библиотеки должны быть размещены в виде исходного кода в каталоге contrib и собираться вместе с ClickHouse. Подробности см. в разделе Рекомендации по добавлению сторонних библиотек.

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

Общие рекомендации

1. Пишите как можно меньше кода.

2. Старайтесь выбирать самое простое решение.

3. Не пишите код, пока не будете понимать, как он будет работать и как будет устроен внутренний цикл.

4. В простейших случаях используйте using вместо классов или структур.

5. По возможности не определяйте конструкторы копирования, операторы присваивания, деструкторы (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), конструкторы перемещения или операторы присваивания перемещением. Другими словами, сгенерированные компилятором функции должны работать корректно. Можно использовать default.

6. Упрощение кода поощряется. По возможности минимизируйте объём кода.

Дополнительные рекомендации

1. Явное указание std:: для типов из stddef.h

не рекомендуется. Другими словами, мы рекомендуем писать size_t вместо std::size_t, потому что так короче.

Тем не менее, допускается добавлять std::.

2. Явное указание std:: для функций из стандартной библиотеки C

не рекомендуется. Другими словами, пишите memcpy вместо std::memcpy.

Причина в том, что есть похожие нестандартные функции, такие как memmem. Мы иногда используем эти функции. Эти функции отсутствуют в namespace std.

Если вы будете везде писать std::memcpy вместо memcpy, то memmem без std:: будет выглядеть странно.

Тем не менее, вы по-прежнему можете использовать std::, если вам так больше нравится.

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

Это допустимо, если так более эффективно.

Например, используйте memcpy вместо std::copy для копирования больших блоков памяти.

4. Многострочные аргументы функций.

Допускается любой из следующих стилей переноса:

function(
  T1 x1,
  T2 x2)
function(
  size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
function(size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
function(size_t left, size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
function(
      size_t left,
      size_t right,
      const & RangesInDataParts ranges,
      size_t limit)