# Как писать код на C++ ## Общее **1.** Этот текст носит рекомендательный характер. **2.** Если вы редактируете код, то имеет смысл писать так, как уже написано. **3.** Стиль нужен для единообразия. Единообразие нужно, чтобы было проще (удобнее) читать код. А также, чтобы было легче осуществлять поиск по коду. **4.** Многие правила продиктованы не какими либо разумными соображениями, а сложившейся практикой. ## Форматирование **1.** Большую часть форматирования сделает автоматически `clang-format`. **2.** Отступы — 4 пробела. Настройте среду разработки так, чтобы таб добавлял четыре пробела. **3.** Открывающая и закрывающие фигурные скобки на отдельной строке. ```cpp inline void readBoolText(bool & x, ReadBuffer & buf) { char tmp = '0'; readChar(tmp, buf); x = tmp != '0'; } ``` **4.** Если всё тело функции — один `statement`, то его можно разместить на одной строке. При этом, вокруг фигурных скобок ставятся пробелы (кроме пробела на конце строки). ```cpp inline size_t mask() const { return buf_size() - 1; } inline size_t place(HashValue x) const { return x & mask(); } ``` **5.** Для функций. Пробелы вокруг скобок не ставятся. ```cpp void reinsert(const Value & x) ``` ```cpp memcpy(&buf[place_value], &x, sizeof(x)); ``` **6.** В выражениях `if`, `for`, `while` и т.д. перед открывающей скобкой ставится пробел (в отличие от вызовов функций). ```cpp for (size_t i = 0; i < rows; i += storage.index_granularity) ``` **7.** Вокруг бинарных операторов (`+`, `-`, `*`, `/`, `%`, ...), а также тернарного оператора `?:` ставятся пробелы. ```cpp 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.** Если ставится перенос строки, то оператор пишется на новой строке, и перед ним увеличивается отступ. ```cpp if (elapsed_ns) message << " (" << rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., " << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) "; ``` **9.** Внутри строки можно, выполнять выравнивание с помощью пробелов. ```cpp dst.ClickLogID = click.LogID; dst.ClickEventID = click.EventID; dst.ClickGoodEvent = click.GoodEvent; ``` **10.** Вокруг операторов `.`, `->` не ставятся пробелы. При необходимости, оператор может быть перенесён на новую строку. В этом случае, перед ним увеличивается отступ. **11.** Унарные операторы `--`, `++`, `*`, `&`, ... не отделяются от аргумента пробелом. **12.** После запятой ставится пробел, а перед — нет. Аналогично для точки с запятой внутри выражения `for`. **13.** Оператор `[]` не отделяется пробелами. **14.** В выражении `template <...>`, между `template` и `<` ставится пробел, а после `<` и до `>` не ставится. ```cpp template <typename TKey, typename TValue> struct AggregatedStatElement {} ``` **15.** В классах и структурах, `public`, `private`, `protected` пишется на том же уровне, что и `class/struct`, а остальной код с отступом. ```cpp template <typename T> class MultiVersion { public: /// Version of object for usage. shared_ptr manage lifetime of version. using Version = std::shared_ptr<const T>; ... } ``` **16.** Если на весь файл один `namespace` и кроме него ничего существенного нет, то отступ внутри `namespace` не нужен. **17.** Если блок для выражения `if`, `for`, `while`, ... состоит из одного `statement`, то фигурные скобки не обязательны. Вместо этого поместите `statement` на отдельную строку. Это правило справедливо и для вложенных `if`, `for`, `while`, ... Если внутренний `statement` содержит фигурные скобки или `else`, то внешний блок следует писать в фигурных скобках. ```cpp /// Finish write. for (auto & stream : streams) stream.second->finalize(); ``` **18.** Не должно быть пробелов на концах строк. **19.** Исходники в кодировке UTF-8. **20.** В строковых литералах можно использовать не-ASCII. ```cpp << ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit."; ``` **21.** Не пишите несколько выражений в одной строке. **22.** Внутри функций группируйте блоки кода, отделяя их не более, чем одной пустой строкой. **23.** Функции, классы, и т. п. отделяются друг от друга одной или двумя пустыми строками. **24.** `const` (относящийся к значению) пишется до имени типа. ```cpp //correct const char * pos const std::string & s //incorrect char const * pos ``` **25.** При объявлении указателя или ссылки, символы `*` и `&` отделяются пробелами с обеих сторон. ```cpp //correct const char * pos //incorrect const char* pos const char *pos ``` **26.** При использовании шаблонных типов, пишите `using` (кроме, возможно, простейших случаев). То есть, параметры шаблона указываются только в `using` и затем не повторяются в коде. `using` может быть объявлен локально, например, внутри функции. ```cpp //correct using FileStreams = std::map<std::string, std::shared_ptr<Stream>>; FileStreams streams; //incorrect std::map<std::string, std::shared_ptr<Stream>> streams; ``` **27.** Нельзя объявлять несколько переменных разных типов в одном выражении. ```cpp //incorrect int x, *y; ``` **28.** C-style cast не используется. ```cpp //incorrect std::cerr << (int)c <<; std::endl; //correct std::cerr << static_cast<int>(c) << std::endl; ``` **29.** В классах и структурах, группируйте отдельно методы и отдельно члены, внутри каждой области видимости. **30.** Для не очень большого класса/структуры, можно не отделять объявления методов от реализации. Аналогично для маленьких методов в любых классах/структурах. Для шаблонных классов/структур, лучше не отделять объявления методов от реализации (так как иначе они всё равно должны быть определены в той же единице трансляции). **31.** Не обязательно умещать код по ширине в 80 символов. Можно в 140. **32.** Всегда используйте префиксный инкремент/декремент, если постфиксный не нужен. ```cpp for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) ``` ## Комментарии **1.** Необходимо обязательно писать комментарии во всех нетривиальных местах. Это очень важно. При написании комментария, можно успеть понять, что код не нужен вообще, или что всё сделано неверно. ```cpp /** Part of piece of memory, that can be used. * For example, if internal_buffer is 1MB, and there was only 10 bytes loaded to buffer from file for reading, * then working_buffer will have size of only 10 bytes * (working_buffer.end() will point to position right after those 10 bytes available for read). */ ``` **2.** Комментарии могут быть сколь угодно подробными. **3.** Комментарии пишутся до соответствующего кода. В редких случаях после, на той же строке. ```cpp /** Parses and executes the query. */ void executeQuery( ReadBuffer & istr, /// Where to read the query from (and data for INSERT, if applicable) WriteBuffer & ostr, /// Where to write the result Context & context, /// DB, tables, data types, engines, functions, aggregate functions... BlockInputStreamPtr & query_plan, /// Here could be written the description on how query was executed QueryProcessingStage::Enum stage = QueryProcessingStage::Complete /// Up to which stage process the SELECT query ) ``` **4.** Комментарии следует писать только на английском языке. **5.** При написании библиотеки, разместите подробный комментарий о том, что это такое, в самом главном заголовочном файле. **6.** Нельзя писать комментарии, которые не дают дополнительной информации. В частности, нельзя писать пустые комментарии вроде этого: ```cpp /* * Procedure Name: * Original procedure name: * Author: * Date of creation: * Dates of modification: * Modification authors: * Original file name: * Purpose: * Intent: * Designation: * Classes used: * Constants: * Local variables: * Parameters: * Date of creation: * Purpose: */ ``` Пример взят с ресурса [http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/](http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/). **7.** Нельзя писать мусорные комментарии (автор, дата создания...) в начале каждого файла. **8.** Однострочные комментарии начинаются с трёх слешей: `///` , многострочные с `/**`. Такие комментарии считаются «документирующими». Замечание: такие комментарии могут использоваться для генерации документации с помощью Doxygen. Но, фактически, Doxygen не используется, так как для навигации по коду гораздо удобнее использовать возможности IDE. **9.** В начале и конце многострочного комментария, не должно быть пустых строк (кроме строки, на которой закрывается многострочный комментарий). **10.** Для закомментированных кусков кода, используются обычные, не "документирующие" комментарии. **11.** Удаляйте закомментированные куски кода перед коммитом. **12.** Не нужно писать нецензурную брань в комментариях или коде. **13.** Не пишите прописными буквами. Не используйте излишнее количество знаков препинания. ```cpp /// WHAT THE FAIL??? ``` **14.** Не составляйте из комментариев строки-разделители. ```cpp ///****************************************************** ``` **15.** Не нужно писать в комментарии диалог (лучше сказать устно). ```cpp /// Why did you do this stuff? ``` **16.** Не нужно писать комментарий в конце блока о том, что представлял собой этот блок. ```cpp /// for ``` ## Имена **1.** В именах переменных и членов класса используйте маленькие буквами с подчёркиванием. ```cpp size_t max_block_size; ``` **2.** Имена функций (методов) camelCase с маленькой буквы. ```cpp std::string getName() const override { return "Memory"; } ``` **3.** Имена классов (структур) - CamelCase с большой буквы. Префиксы кроме I для интерфейсов - не используются. ```cpp class StorageMemory : public IStorage ``` **4.** `using` называются также, как классы, либо с `_t` на конце. **5.** Имена типов — параметров шаблонов: в простых случаях - `T`; `T`, `U`; `T1`, `T2`. В более сложных случаях - либо также, как имена классов, либо можно добавить в начало букву `T`. ```cpp template <typename TKey, typename TValue> struct AggregatedStatElement ``` **6.** Имена констант — параметров шаблонов: либо также, как имена переменных, либо `N` в простом случае. ```cpp template <bool without_www> struct ExtractDomain ``` **7.** Для абстрактных классов (интерфейсов) можно добавить в начало имени букву `I`. ```cpp class IBlockInputStream ``` **8.** Если переменная используется достаточно локально, то можно использовать короткое имя. В остальных случаях используйте имя, описывающее смысл. ```cpp bool info_successfully_loaded = false; ``` **9.** В именах `define` и глобальных констант используется ALL_CAPS с подчёркиванием. ```cpp #define MAX_SRC_TABLE_NAMES_TO_STORE 1000 ``` **10.** Имена файлов с кодом называйте по стилю соответственно тому, что в них находится. Если в файле находится один класс, назовите файл, как класс (CamelCase). Если в файле находится одна функция, назовите файл, как функцию (camelCase). **11.** Если имя содержит сокращение, то: - для имён переменных, всё сокращение пишется маленькими буквами `mysql_connection` (не `mySQL_connection`). - для имён классов и функций, сохраняются большие буквы в сокращении `MySQLConnection` (не `MySqlConnection`). **12.** Параметры конструктора, использующиеся сразу же для инициализации соответствующих членов класса, следует назвать также, как и члены класса, добавив подчёркивание в конец. ```cpp 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.** Именование локальных переменных и членов класса никак не отличается (никакие префиксы не нужны). ```cpp timer (not m_timer) ``` **14.** Константы в `enum` — CamelCase с большой буквы. Также допустим ALL_CAPS. Если `enum` не локален, то используйте `enum class`. ```cpp enum class CompressionMethod { QuickLZ = 0, LZ4 = 1, }; ``` **15.** Все имена - по-английски. Транслит с русского использовать нельзя. ```text не Stroka ``` **16.** Сокращения (из нескольких букв разных слов) в именах можно использовать только если они являются общепринятыми (если для сокращения можно найти расшифровку в английской википедии или сделав поисковый запрос). ```text `AST`, `SQL`. Не `NVDH` (что-то неведомое) ``` Сокращения в виде обрезанного слова можно использовать, только если такое сокращение является широко используемым. Впрочем, сокращения также можно использовать, если расшифровка находится рядом в комментарии. **17.** Имена файлов с исходниками на C++ должны иметь расширение только `.cpp`. Заголовочные файлы - только `.h`. ## Как писать код **1.** Управление памятью. Ручное освобождение памяти (`delete`) можно использовать только в библиотечном коде. В свою очередь, в библиотечном коде, оператор `delete` можно использовать только в деструкторах. В прикладном коде следует делать так, что память освобождается каким-либо объектом, который владеет ей. Примеры: - проще всего разместить объект на стеке, или сделать его членом другого класса. - для большого количества маленьких объектов используйте контейнеры. - для автоматического освобождения маленького количества объектов, выделенных на куче, используйте `shared_ptr/unique_ptr`. **2.** Управление ресурсами. Используйте `RAII` и см. пункт выше. **3.** Обработка ошибок. Используйте исключения. В большинстве случаев, нужно только кидать исключения, а ловить - не нужно (потому что `RAII`). В программах офлайн обработки данных, зачастую, можно не ловить исключения. В серверах, обрабатывающих пользовательские запросы, как правило, достаточно ловить исключения на самом верху обработчика соединения. В функциях потока, следует ловить и запоминать все исключения, чтобы выкинуть их в основном потоке после `join`. ```cpp /// Если вычислений ещё не было - вычислим первый блок синхронно if (!started) { calculate(); started = true; } else /// Если вычисления уже идут - подождём результата pool.wait(); if (exception) exception->rethrow(); ``` Ни в коем случае не «проглатывайте» исключения без разбора. Ни в коем случае, не превращайте все исключения без разбора в сообщения в логе. ```cpp //Not correct catch (...) {} ``` Если вам нужно проигнорировать какие-то исключения, то игнорируйте только конкретные, а остальные кидайте обратно. ```cpp catch (const DB::Exception & e) { if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION) return nullptr; else throw; } ``` При использовании функций, использующих коды возврата или `errno`, проверяйте результат и кидайте исключение. ```cpp if (0 != close(fd)) throwFromErrno("Cannot close file " + file_name, ErrorCodes::CANNOT_CLOSE_FILE); ``` `assert` не используются. **4.** Типы исключений. В прикладном коде не требуется использовать сложную иерархию исключений. Желательно, чтобы текст исключения был понятен системному администратору. **5.** Исключения, вылетающие из деструкторов. Использовать не рекомендуется, но допустимо. Используйте следующие варианты: - Сделайте функцию (`done()` или `finalize()`), которая позволяет заранее выполнить всю работу, в процессе которой может возникнуть исключение. Если эта функция была вызвана, то затем в деструкторе не должно возникать исключений. - Слишком сложную работу (например, отправку данных по сети) можно вообще не делать в деструкторе, рассчитывая, что пользователь заранее позовёт метод для завершения работы. - Если в деструкторе возникло исключение, желательно не "проглатывать" его, а вывести информацию в лог (если в этом месте доступен логгер). - В простых программах, если соответствующие исключения не ловятся, и приводят к завершению работы с записью информации в лог, можно не беспокоиться об исключениях, вылетающих из деструкторов, так как вызов `std::terminate` (в случае `noexcept` по умолчанию в C++11), является приемлемым способом обработки исключения. **6.** Отдельные блоки кода. Внутри одной функции, можно создать отдельный блок кода, для того, чтобы сделать некоторые переменные локальными в нём, и для того, чтобы соответствующие деструкторы были вызваны при выходе из блока. ```cpp Block block = data.in->read(); { std::lock_guard<std::mutex> lock(mutex); data.ready = true; data.block = block; } ready_any.set(); ``` **7.** Многопоточность. В программах офлайн обработки данных: - cначала добейтесь более-менее максимальной производительности на одном процессорном ядре, потом можно распараллеливать код, но только если есть необходимость. В программах - серверах: - используйте пул потоков для обработки запросов. На данный момент, у нас не было задач, в которых была бы необходимость использовать userspace context switching. Fork для распараллеливания не используется. **8.** Синхронизация потоков. Часто можно сделать так, чтобы отдельные потоки писали данные в разные ячейки памяти (лучше в разные кэш-линии), и не использовать синхронизацию потоков (кроме `joinAll`). Если синхронизация нужна, то в большинстве случаев, достаточно использовать mutex под `lock_guard`. В остальных случаях, используйте системные примитивы синхронизации. Не используйте busy wait. Атомарные операции можно использовать только в простейших случаях. Не нужно писать самостоятельно lock-free структуры данных, если вы не являетесь экспертом. **9.** Ссылки и указатели. В большинстве случаев, предпочитайте ссылки. **10.** const. Используйте константные ссылки, указатели на константу, `const_iterator`, константные методы. Считайте, что `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::string`). Если функция захватывает владение объектом, созданным на куче, то сделайте типом аргумента `shared_ptr` или `unique_ptr`. **14.** Возврат значений. В большинстве случаев, просто возвращайте значение с помощью `return`. Не пишите `[return std::move(res)]{.strike}`. Если внутри функции создаётся объект на куче и отдаётся наружу, то возвращайте `shared_ptr` или `unique_ptr`. В некоторых редких случаях, может потребоваться возвращать значение через аргумент функции. В этом случае, аргументом будет ссылка. ```cpp 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`. ```cpp 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` для функций, если в этом нет необходимости. ```cpp [auto f() -> void;]{.strike} ``` **25.** Объявление и инициализация переменных. ```cpp //right way std::string s = "Hello"; std::string s{"Hello"}; //wrong way auto s = std::string{"Hello"}; ``` **26.** Для виртуальных функций, пишите `virtual` в базовом классе, а в классах-наследниках, пишите `override` и не пишите `virtual`. ## Неиспользуемые возможности языка C++ **1.** Виртуальное наследование не используется. **2.** Спецификаторы исключений из C++03 не используются. ## Платформа **1.** Мы пишем код под конкретную платформу. Хотя, при прочих равных условиях, предпочитается более-менее кроссплатформенный или легко портируемый код. **2.** Язык - C++17. **3.** Компилятор - `gcc`. На данный момент (декабрь 2017), код собирается версией 7.2. (Также код может быть собран `clang 5`) Используется стандартная библиотека (реализация `libstdc++` или `libc++`). **4.** ОС - Linux Ubuntu, не более старая, чем Precise. **5.** Код пишется под процессор с архитектурой x86_64. Набор инструкций минимальный из поддержаных нашими серверами. Сейчас это - SSE4.2. **6.** Используются флаги компиляции `-Wall -Wextra -Werror`. **7.** Используется статическая линковка со всеми библиотеками кроме тех, которые трудно подключить статически (см. вывод команды `ldd`). **8.** Код разрабатывается и отлаживается с релизными параметрами сборки. ## Инструментарий **1.** Хорошая среда разработки - KDevelop. **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++14 (допустимо использовать экспериментальные расширения) а также фреймворки `boost`, `Poco`. **2.** При необходимости, можно использовать любые известные библиотеки, доступные в ОС из пакетов. Если есть хорошее готовое решение, то оно используется, даже если для этого придётся установить ещё одну библиотеку. (Но будьте готовы к тому, что иногда вам придётся выкидывать плохие библиотеки из кода.) **3.** Если в пакетах нет нужной библиотеки, или её версия достаточно старая, или если она собрана не так, как нужно, то можно использовать библиотеку, устанавливаемую не из пакетов. **4.** Если библиотека достаточно маленькая и у неё нет своей системы сборки, то следует включить её файлы в проект, в директорию `contrib`. **5.** Предпочтение всегда отдаётся уже использующимся библиотекам. ## Общее **1.** Пишите как можно меньше кода. **2.** Пробуйте самое простое решение. **3.** Не нужно писать код, если вы ещё не знаете, что будет делать ваша программа, и как будет работать её внутренний цикл. **4.** В простейших случаях, используйте `using` вместо классов/структур. **5.** Если есть возможность - не пишите конструкторы копирования, операторы присваивания, деструктор (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), move-конструкторы и move-присваивания. То есть, чтобы соответствущие функции, генерируемые компилятором, работали правильно. Можно использовать `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.** Перенос длинных аргументов функций. Допустимо использовать любой стиль переноса, похожий на приведённые ниже: ```cpp function( T1 x1, T2 x2) ``` ```cpp function( size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) ``` ```cpp function(size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) ``` ```cpp function(size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) ``` ```cpp function( size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) ``` [Оригинальная статья](https://clickhouse.tech/docs/ru/development/style/) <!--hide-->