.. role:: strike :class: strike Как писать код на C++ ===================== Общее ----- #. Этот текст носит рекомендательный характер. #. Всё относится только к команде разработки движка Яндекс.Метрики и ClickHouse. #. Если вы редактируйте не наш код, то имеет смысл писать так, как уже написано. #. Стиль нужен для единообразия. Единообразие нужно, чтобы было проще (удобнее) читать код. А также, чтобы было легче осуществлять поиск по коду. #. Многие правила продиктованы не какими либо разумными соображениями, а сложившейся практикой. Форматирование -------------- #. Большую часть форматирования сделает автоматически clang-format. Инструкция для подключения clang-format в kdevelop описана в файле format_sources #. Отступы - 4 пробела. Настройте среду разработки так, чтобы таб добавлял четыре пробела. #. Открывающая фигурная скобка на новой, отдельной строке. (Закрывающая - тоже.) .. code-block:: cpp inline void readBoolText(bool & x, ReadBuffer & buf) { char tmp = '0'; readChar(tmp, buf); x = tmp != '0'; } #. Но если всё тело функции достаточно короткое (один statement) - при желании, его можно целиком разместить на одной строке. При этом, вокруг фигурных скобок ставятся пробелы (кроме пробела на конце строки). .. code-block:: cpp inline size_t mask() const { return buf_size() - 1; } inline size_t place(HashValue x) const { return x & mask(); } #. Для функций, пробелы вокруг скобок не ставятся. .. code-block:: cpp void reinsert(const Value & x) .. code-block:: cpp memcpy(&buf[place_value], &x, sizeof(x)); #. При использовании выражений if, for, while, ... (в отличие от вызовов функций) перед открывающей скобкой ставится пробел. .. code-block:: cpp for (size_t i = 0; i < rows; i += storage.index_granularity) #. Вокруг бинарных операторов (+, -, \*, /, %, ...), а также тернарного оператора ?: ставятся пробелы. .. code-block:: 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'); #. Если ставится перенос строки, то оператор пишется на новой строке, и перед ним увеличивается отступ. .. code-block:: cpp if (elapsed_ns) message << " (" << rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., " << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) "; #. Внутри строки можно, при желании, выполнять выравнивание с помощью пробелов. .. code-block:: cpp dst.ClickLogID = click.LogID; dst.ClickEventID = click.EventID; dst.ClickGoodEvent = click.GoodEvent; #. Вокруг операторов ``.``, ``->`` не ставятся пробелы. При необходимости, оператор может быть перенесён на новую строку. В этом случае, перед ним увеличивается отступ. #. Унарные операторы (``--, ++, *, &``, ...) не отделяются от аргумента пробелом. #. После запятой ставится пробел, а перед - нет. Аналогично для точки с запятой внутри выражения for. #. Оператор ``[]`` не отделяется пробелами. #. В выражении ``template <...>``, между ``template`` и ``<`` ставится пробел; после ``<`` и до ``>`` - не ставится. .. code-block:: cpp template struct AggregatedStatElement #. В классах и структурах, public, private, protected пишется на том же уровне, что и class/struct, а все остальные внутренности - глубже. .. code-block:: cpp template > class MultiVersion { public: /// Конкретная версия объекта для использования. shared_ptr определяет время жизни версии. using Version = Ptr; #. Если на весь файл один namespace и кроме него ничего существенного нет - то отступ внутри namespace не нужен. #. Если блок для выражения if, for, while... состоит из одного statement-а, то фигурные скобки писать не обязательно. Вместо этого поместите statement на отдельную строку. Этим statement-ом также может быть вложенный if, for, while... Но если внутренний statement содержит фигурные скобки или else, то у внешнего блок следует писать в фигурных скобках. .. code-block:: cpp /// Если файлы не открыты, то открываем их. if (streams.empty()) for (const auto & name : column_names) streams.emplace(name, std::make_unique( storage.files[name].data_file.path(), storage.files[name].marks[mark_number].offset)); #. Не должно быть пробелов на концах строк. #. Исходники в кодировке UTF-8. #. В строковых литералах можно использовать не-ASCII. .. code-block:: cpp << ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit."; #. Не пишите несколько выражений в одной строке. #. Внутри функций, группируйте куски кода, отделяя их не более, чем одной пустой строкой. #. Функции, классы, и т. п. отделяются друг от друга минимум одной, максимум двумя пустыми строками. #. const (относящийся к значению) пишется до имени типа. .. code-block:: cpp const char * pos .. code-block:: cpp const std::string & s :strike:`char const * pos` #. При объявлении указателя или ссылки, символы * и & отделяются пробелами с обеих сторон. .. code-block:: cpp const char * pos :strike:`const char\* pos` :strike:`const char \*pos` #. При использовании шаблонных типов, пишите using (кроме, возможно, простейших случаев). То есть, параметры шаблона указываются только в using-е и затем не повторяются в коде. using может быть объявлен локально, например, внутри функции. .. code-block:: cpp using FileStreams = std::map>; FileStreams streams; :strike:`std::map> streams;` #. Нельзя объявлять несколько переменных разных типов в одном объявлении. :strike:`int x, *y;` #. c-style cast не используется. :strike:`std::cerr << (int)c << std::endl;` .. code-block:: cpp std::cerr << static_cast(c) << std::endl; #. В классах и структурах, группируйте отдельно методы и отдельно члены, внутри каждой области видимости. #. Для не очень большого класса/структуры, можно не отделять объявления методов от реализации. Аналогично для маленьких методов в любых классах/структурах. Для шаблонных классов/структур, лучше не отделять объявления методов от реализации (так как иначе они всё равно должны быть определены в той же единице трансляции). #. Не обязательно умещать код по ширине в 80 символов. Можно в 140. #. Всегда используйте префиксный инкремент/декремент, если постфиксный не нужен. .. code-block:: cpp for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) Комментарии ----------- #. Необходимо обязательно писать комментарии во всех нетривиальных местах. Это очень важно. При написании комментария, можно успеть понять, что код не нужен вообще, или что всё сделано неверно. .. code-block:: cpp /** Часть куска памяти, которую можно использовать. * Например, если internal_buffer - 1MB, а из файла для чтения было загружено в буфер * только 10 байт, то working_buffer будет иметь размер 10 байт * (working_buffer.end() будет указывать на позицию сразу после тех 10 байт, которых можно прочитать). */ #. Комментарии могут быть сколь угодно подробными. #. Комментарии пишутся до соответствующего кода. В редких случаях - после, на той же строке. .. code-block:: text /** Парсит и исполняет запрос. */ void executeQuery( ReadBuffer & istr, /// Откуда читать запрос (а также данные для INSERT-а, если есть) WriteBuffer & ostr, /// Куда писать результат Context & context, /// БД, таблицы, типы данных, движки таблиц, функции, агрегатные функции... BlockInputStreamPtr & query_plan, /// Сюда может быть записано описание, как выполнялся запрос QueryProcessingStage::Enum stage = QueryProcessingStage::Complete); /// До какой стадии выполнять SELECT запрос. #. Комментарии следует писать только на английском языке. #. При написании библиотеки, разместите подробный комментарий о том, что это такое, в самом главном заголовочном файле. #. Нельзя писать комментарии, которые не дают дополнительной информации. В частности, *НЕЛЬЗЯ* писать пустые комментарии. .. code-block:: 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/) #. Нельзя писать мусорные комментарии (автор, дата создания...) в начале каждого файла. #. Однострочные комментарии начинаются с трёх слешей: ``///``, многострочные - с ``/**``. Такие комментарии считаются "документрующими". Замечание: такие комментарии могут использоваться для генерации документации с помощью Doxygen. Но, фактически, Doxygen не используется, так как для навигации по коду гораздо удобне использовать возможности IDE. #. В начале и конце многострочного комментария, не должно быть пустых строк (кроме строки, на которой закрывается многострочный комментарий). #. Для закомментированных кусков кода, используются обычные, не "документирующие" комментарии. Удаляйте закомментированные куски кода перед коммитом. #. Не нужно писать нецензурную брань в комментариях. #. Не нужно писать в комментариях слишком много восклицательных знаков или знаков вопроса, или выделять слишком много слов большими буквами. :strike:`/// WHAT THE FAIL???` #. Не нужно составлять из комментариев строки-разделители. :strike:`/*******************************************************/` #. Не нужно писать в комментарии диалог (лучше сказать устно). :strike:`/// Зачем ты сделал эту фигню?` #. Не нужно писать комментарий в конце блока о том, что представлял собой этот блок. :strike:`} /// for` Имена ----- #. Имена переменных и членов класса - маленькими буквами с подчёркиванием. .. code-block:: cpp size_t max_block_size; #. Имена функций (методов) - camelCase с маленькой буквы. .. code-block:: cpp std::string getName() const override { return "Memory"; } #. Имена классов (структур) - CamelCase с большой буквы. Префиксы кроме I для интерфейсов - не используются. .. code-block:: cpp class StorageMemory : public IStorage #. Имена using-ов - также, как классов, либо можно добавить _t на конце. #. Имена типов - параметров шаблонов: в простых случаях - T; T, U; T1, T2. В более сложных случаях - либо также, как имена классов, либо можно добавить в начало букву T. .. code-block:: cpp template struct AggregatedStatElement #. Имена констант - параметров шаблонов: либо также, как имена переменных, либо N - в простом случае. .. code-block:: cpp template struct ExtractDomain #. Для абстрактных классов (интерфейсов) можно добавить в начало имени букву I. .. code-block:: cpp class IBlockInputStream #. Если переменная используется достаточно локально, то можно использовать короткое имя. В остальных случаях - используйте достаточно подробное имя, описывающее смысл. .. code-block:: cpp bool info_successfully_loaded = false; #. define-ы - ALL_CAPS с подчёркиванием. Глобальные константы - тоже. .. code-block:: cpp #define MAX_SRC_TABLE_NAMES_TO_STORE 1000 #. Имена файлов с кодом называйте по стилю соответственно тому, что в них находится. Если в файле находится один класс - назовите файл, как класс - в CamelCase. Если в файле находится одна функция - назовите файл, как функцию - в camelCase. #. Если имя содержит сокращение, то: * для имён переменных, всё сокращение пишется маленькими буквами; ``mysql_connection`` :strike:`mySQL_connection` * для имён классов и функций, сохраняются большие буквы в сокращении. ``MySQLConnection`` :strike:`MySqlConnection` #. Параметры конструктора, использующиеся сразу же для инициализации соответствующих членов класса, следует назвать также, как и члены класса, добавив подчёркивание в конец. .. code-block:: cpp FileQueueProcessor( const std::string & path_, const std::string & prefix_, std::shared_ptr handler_) : path(path_), prefix(prefix_), handler(handler_), log(&Logger::get("FileQueueProcessor")) { } Также можно называть параметры конструктора так же, как и члены класса (не добавлять подчёркивание), но только если этот параметр не используется в теле конструктора. #. Именование локальных переменных и членов класса никак не отличается (никакие префиксы не нужны). ``timer`` :strike:`m_timer` #. Константы в enum-е - CamelCase с большой буквы. Также допустимо ALL_CAPS. Если enum не локален, то используйте enum class. .. code-block:: cpp enum class CompressionMethod { QuickLZ = 0, LZ4 = 1, }; #. Все имена - по английски. Транслит с русского использовать нельзя. :strike:`Stroka` #. Сокращения (из нескольких букв разных слов) в именах можно использовать только если они являются общепринятыми (если для сокращения можно найти расшифровку в английской википедии или сделав поисковый запрос). ``AST`` ``SQL`` :strike:`NVDH (неведомая х.)` Сокращения в виде обрезанного слова можно использовать, только если такое сокращение является широко используемым. Впрочем, сокращения также можно использовать, если расшифровка находится рядом в комментарии. #. Имена файлов с исходниками на C++ должны иметь расширение только .cpp. Заголовочные файлы - только .h. :strike:`.hpp` :strike:`.cc` :strike:`.C` :strike:`.inl` Можно ``.inl.h``, но не :strike:`.h.inl:strike:` Как писать код -------------- #. Управление памятью. Ручное освобождение памяти (delete) можно использовать только в библиотечном коде. В свою очередь, в библиотечном коде, оператор delete можно использовать только в деструкторах. В прикладном коде следует делать так, что память освобождается каким-либо объектом, который владеет ей. Примеры: * проще всего разместить объект на стеке, или сделать его членом другого класса. * для большого количества маленьких объектов используйте контейнеры. * для автоматического освобождения маленького количества объектов, выделенных на куче, используйте shared_ptr/unique_ptr. #. Управление ресурсами. Используйте RAII и см. пункт выше. #. Обработка ошибок. Используйте исключения. В большинстве случаев, нужно только кидать исключения, а ловить - не нужно (потому что RAII). В программах offline обработки данных, зачастую, можно не ловить исключения. В серверах, обрабатывающих пользовательские запросы, как правило, достаточно ловить исключения на самом верху обработчика соединения. В функциях потока, следует ловить и запоминать все исключения, чтобы выкинуть их в основном потоке после join. .. code-block:: cpp /// Если вычислений ещё не было - вычислим первый блок синхронно if (!started) { calculate(); started = true; } else /// Если вычисления уже идут - подождём результата pool.wait(); if (exception) exception->rethrow(); Ни в коем случае не "проглатывайте" исключения без разбора. Ни в коем случае, не превращайте все исключения без разбора в сообщения в логе. :strike:`catch (...) {}` Если вам нужно проигнорировать какие-то исключения, то игнорируйте только конкретные, а остальные - кидайте обратно. .. code-block:: cpp catch (const DB::Exception & e) { if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION) return nullptr; else throw; } При использовании функций, использующих коды возврата или errno - проверяйте результат и кидайте исключение. .. code-block:: cpp if (0 != close(fd)) throwFromErrno("Cannot close file " + file_name, ErrorCodes::CANNOT_CLOSE_FILE); assert-ы не используются. #. Типы исключений. В прикладном коде не требуется использовать сложную иерархию исключений. Желательно, чтобы текст исключения был понятен системному администратору. #. Исключения, вылетающие из деструкторов. Использовать не рекомендуется, но допустимо. Используйте следующие варианты: * Сделайте функцию (done() или finalize()), которая позволяет заранее выполнить всю работу, в процессе которой может возникнуть исключение. Если эта функция была вызвана, то затем в деструкторе не должно возникать исключений. * Слишком сложную работу (например, отправку данных по сети) можно вообще не делать в деструкторе, рассчитывая, что пользователь заранее позовёт метод для завершения работы. * Если в деструкторе возникло исключение, желательно не "проглатывать" его, а вывести информацию в лог (если в этом месте доступен логгер). * В простых программах, если соответствующие исключения не ловятся, и приводят к завершению работы с записью информации в лог, можно не беспокоиться об исключениях, вылетающих из деструкторов, так как вызов std::terminate (в случае noexcept по-умолчанию в C++11), является приемлимым способом обработки исключения. #. Отдельные блоки кода. Внутри одной функции, можно создать отдельный блок кода, для того, чтобы сделать некоторые переменные локальными в нём, и для того, чтобы соответствующие деструкторы были вызваны при выходе из блока. .. code-block:: cpp Block block = data.in->read(); { std::lock_guard lock(mutex); data.ready = true; data.block = block; } ready_any.set(); #. Многопоточность. В программах offline обработки данных: * cначала добейтесь более-менее максимальной производительности на одном процессорном ядре; * потом можно распараллеливать код, но только если есть необходимость. В программах - серверах: * используйте пул потоков для обработки запросов; * на данный момент, у нас не было задач, в которых была бы необходимость использовать userspace context switching. Fork для распараллеливания не используется. #. Синхронизация потоков. Часто можно сделать так, чтобы отдельные потоки писали данные в разные ячейки памяти (лучше - в разные кэш-линии), и не использовать синхронизацию потоков (кроме joinAll). Если синхронизация нужна, то в большинстве случаев, достаточно использовать mutex под lock_guard-ом. В остальных случаях, используйте системные примитивы синхронизации. Не используйте busy wait. Атомарные операции можно использовать только в простейших случаях. Не нужно писать самостоятельно lock-free структуры данных, если вы не являетесь экспертом. #. Ссылки и указатели. В большинстве случаев, предпочитайте ссылки. #. const. Используйте константные ссылки, указатели на константу, const_iterator, константные методы. Считайте, что const - вариант написания "по-умолчанию", а отсутствие const - только при необходимости. Для переменных, передающихся по значению, использовать const обычно не имеет смысла. #. unsigned. Используйте unsigned, если нужно. #. Числовые типы. Используйте типы UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, а также size_t, ssize_t, ptrdiff_t. Не используйте для чисел типы signed/unsigned long, long long, short; signed char, unsigned char, а также char. #. Передача аргументов. Сложные значения передавайте по ссылке (включая std::string). Если функция захватывает владение объектом, созданным на куче, то сделайте типом аргумента shared_ptr или unique_ptr. #. Возврат значений. В большинстве случаев, просто возвращайте значение с помощью return. Не пишите :strike:`return std::move(res)`. Если внутри функции создаётся объект на куче и отдаётся наружу, то возвращайте shared_ptr или unique_ptr. В некоторых редких случаях, может потребоваться возвращать значение через аргумент функции. В этом случае, аргументом будет ссылка. .. code-block:: cpp using AggregateFunctionPtr = std::shared_ptr; /** Позволяет создать агрегатную функцию по её имени. */ class AggregateFunctionFactory { public: AggregateFunctionFactory(); AggregateFunctionPtr get(const String & name, const DataTypes & argument_types) const; #. namespace. Для прикладного кода отдельный namespace использовать не нужно. Для маленьких библиотек - не требуется. Для не совсем маленьких библиотек - поместите всё в namespace. Внутри библиотеки в .h файле можно использовать namespace detail для деталей реализации, не нужных прикладному коду. В .cpp файле можно использовать static или анонимный namespace для скрытия символов. Также, namespace можно использовать для enum, чтобы соответствующие имена не попали во внешний namespace (но лучше использовать enum class). #. Отложенная инициализация. Обычно, если для инициализации требуются аргументы, то не пишите конструктор по-умопчанию. Если потом вам потребовалась отложенная инициализация, то вы можете дописать конструктор по-умолчанию (который создаст объект с некорректным состоянием). Или, для небольшого количества объектов, можно использовать shared_ptr/unique_ptr. .. code-block:: cpp Loader(DB::Connection * connection_, const std::string & query, size_t max_block_size_); /// Для отложенной инициализации Loader() {} #. Виртуальные функции. Если класс не предназначен для полиморфного использования, то не нужно делать функции виртуальными зря. Это относится и к деструктору. #. Кодировки. Везде используется UTF-8. Используется ``std::string``, ``char *``. Не используется ``std::wstring``, ``wchar_t``. #. Логгирование. См. примеры везде в коде. Перед коммитом, удалите всё бессмысленное и отладочное логгирование, и другие виды отладочного вывода. Не должно быть логгирования на каждую итерацию внутреннего цикла, даже уровня Trace. При любом уровне логгирования, логи должно быть возможно читать. Логгирование следует использовать, в основном, только в прикладном коде. Сообщения в логе должны быть написаны на английском языке. Желательно, чтобы лог был понятен системному администратору. Не нужно писать ругательства в лог. В логе используется кодировка UTF-8. Изредка можно использовать в логе не-ASCII символы. #. Ввод-вывод. Во внутренних циклах (в критичных по производительности участках программы) нельзя использовать iostreams (в том числе, ни в коем случае не используйте stringstream). Вместо этого используйте библиотеку DB/IO. #. Дата и время. См. библиотеку DateLUT. #. include. В заголовочном файле используется только ``#pragma once``, а include guard-ы писать не нужно. #. using. using namespace не используется. using что-то конкретное - можно. Лучше локально - внутри класса или функции. #. Не нужно использовать trailing return type для функций, если в этом нет необходимости. :strike:`auto f() -> void;` #. Не нужно объявлять и инициализировать переменные так: :strike:`auto s = std::string{"Hello"};` Надо так: ``std::string s = "Hello";`` ``std::string s{"Hello"};`` #. Для виртуальных функций, пишите virtual в базовом классе, а в классах-наследниках, пишите override и не пишите virtual. Неиспользуемые возможности языка C++ ------------------------------------ #. Виртуальное наследование не используется. #. Спецификаторы исключений из C++03 не используются. #. Function try block не используется, за исключением функции main в тестах. Платформа --------- #. Мы пишем некроссплатформенный код (под конкретную платформу). Хотя, при прочих равных условиях, предпочитается более-менее кроссплатформенный или легко портируемый код. #. Язык - C++17. Возможно использование расширений GNU при необходимости. #. Компилятор - gcc. На данный момент (апрель 2017), код собирается версией 6.3. (Также код может быть собран clang 4) Используется стандартная библиотека от gcc. #. ОС - Linux Ubuntu, не более старая, чем Precise. #. Код пишется под процессор с архитектурой x86_64. Набор инструкций - минимальный поддерживаемый среди наших серверов. Сейчас это - SSE4.2. #. Используются флаги компиляции ``-Wall -Werror``. #. Используется статическая линковка со всеми библиотеками кроме тех, которые трудно подключить статически (см. вывод команды ldd). #. Код разрабатывается и отлаживается с релизными параметрами сборки. Инструментарий -------------- #. Хорошая среда разработки - KDevelop. #. Для отладки используется gdb, valgrind (memcheck), strace, -fsanitize=..., tcmalloc_minimal_debug. #. Для профилирования используется Linux Perf, valgrind (callgrind), strace -cf. #. Исходники в Git. #. Сборка с помощью CMake. #. Программы выкладываются с помощью deb пакетов. #. Коммиты в master не должны ломать сборку проекта. А работоспособность собранных программ гарантируется только для отдельных ревизий. #. Коммитьте как можно чаще, в том числе и не рабочий код. Для этого следует использовать бранчи. Если ваш код в master-е ещё не собирается, перед push-ем - исключите его из сборки; также вы будете должны его доработать или удалить в течение нескольких дней. #. Для нетривиальных изменений, используются бранчи. Следует загружать бранчи на сервер. #. Ненужный код удаляется из исходников. Библиотеки ---------- #. Используются стандартная библиотека C++14 (допустимо использовать experimental расширения) а также фреймворки boost, Poco. #. При необходимости, можно использовать любые известные библиотеки, доступные в ОС из пакетов. Если есть хорошее готовое решение, то оно используется, даже если для этого придётся установить ещё одну библиотеку. (Но будьте готовы к тому, что иногда вам придётся выкидывать плохие библиотеки из кода.) #. Если в пакетах нет нужной библиотеки, или её версия достаточно старая, или если она собрана не так, как нужно, то можно использовать библиотеку, устанавливаемую не из пакетов. #. Если библиотека достаточно маленькая и у неё нет своей системы сборки, то следует включить её файлы в проект, в директорию contrib. #. Предпочтение всегда отдаётся уже использующимся библиотекам. Общее ----- #. Пишите как можно меньше кода. #. Пробуйте самое простое решение. #. Не нужно писать код, если вы ещё не знаете, что будет делать ваша программа, и как будет работать её внутренний цикл. #. В простейших случаях, используйте using вместо классов/структур. #. Если есть возможность - не пишите конструкторы копирования, операторы присваивания, деструктор (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), move-конструкторы и move-присваивания. То есть, чтобы соответствущие функции, генерируемые компилятором, работали правильно. Можно использовать default. #. Приветствуется упрощение и уменьшение объёма кода. Дополнительно ------------- #. Явное указание std:: для типов из stddef.h. Рекомендуется не указывать. То есть, рекомендуется писать size_t вместо std::size_t - потому что это короче. Но при желании, вы можете всё-таки приписать std:: - такой вариант тоже допустим. #. Явное указание std:: для функций из стандартной библиотеки C. Не рекомендуется. То есть, пишите memcpy вместо std::memcpy. Причина - существуют похожие нестандартные функции, например, memmem. Мы можем использовать и изредка используем эти функции. Эти функции отсутствуют в namespace std. Если вы везде напишете std::memcpy вместо memcpy, то будет неудобно смотреться memmem без std::. Тем не менее, указывать std:: тоже допустимо, если так больше нравится. #. Использование функций из C при наличии аналогов в стандартной библиотеке C++. Допустимо, если это использование эффективнее. Для примера, для копирования длинных кусков памяти, используйте memcpy вместо std::copy. #. Перенос длинных аргументов функций. Допустимо использовать любой стиль переноса, похожий на приведённые ниже: .. code-block:: cpp function( T1 x1, T2 x2) .. code-block:: cpp function( size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) .. code-block:: cpp function(size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) .. code-block:: cpp function(size_t left, size_t right, const & RangesInDataParts ranges, size_t limit) .. code-block:: cpp function( size_t left, size_t right, const & RangesInDataParts ranges, size_t limit)