Часть 1. Введение
… Часть 6. Особенности Google Часть 7. Дополнительные возможности C++ Часть 8. Именование
…
Это последняя переведенная часть руководства по стилю Google C++.
Спасибо за ваши комментарии и замечания по переводу.
Я надеюсь, что это руководство станет полезным инструментом для программистов C++.
Оригинальная статья (форк на github), обновленный перевод .
И букв здесь очень много.
Дополнительные возможности C++
Ссылки на Rvalue
Используйте ссылки rvalue:- Объявить конструкторы перемещения и операторы перемещения.
- Для объявления перегруженные функции с аргументами const& и &&, если это обеспечит значительное повышение производительности по сравнению с передачей по значению, или если вы пишете код с небольшими накладными расходами или поддержкой произвольных типов.
Избегайте увеличения количества перегруженных функций (обычно это происходит при объединении типов для нескольких параметров).
- Для поддержки «идеальной пересылки» в «универсальном» коде.
Синтаксис аналогичен обычной ссылке.
Например, void f(std::string&& s); объявляет функцию со ссылкой rvalue на std::string. Когда суффикс «&&» (без дополнительных квалификаторов) используется с аргументом шаблона функции, применяются специальные правила для определения типа аргумента.
Такая ссылка называется пересылкой.
Позади
- Определение конструктора перемещения (с использованием ссылки rvalue на тип класса) позволяет перемещать класс, а не копировать его.
Например, если v1 Этот станд::вектор , то код авто v2(std::move(v1)) скорее всего, вместо копирования большого объема данных выполнит несколько операций с указателем.
И в большинстве случаев это приведет к значительному увеличению производительности кода.
- Ссылки Rvalue позволяют реализовать типы, которые можно перемещать, а не копировать.
Это полезно для типов, которые невозможно скопировать, но которые вы хотите передать функции в качестве аргумента, сохранить в контейнере и т. д.
- Функция станд::переместить необходимо для эффективного использования некоторых типов стандартной библиотеки, таких как std::unique_ptr .
- Пересылка ссылок , которые используют объявление ссылки rvalue, позволяют вам написать одну оболочку, которая перемещает аргумент в другую функцию.
И это работает одинаково независимо от того, является ли объект временным или нет, постоянным или нет. Это называется «идеальная переадресация».
- Ссылки Rvalue не все хорошо понимают. Такие функции, как свертывание ссылок или специальные правила определения типов, сильно усложняют ситуацию.
- Ссылки Rvalue часто используются неправильно, потому что.
они не интуитивно понятны: обычно ожидается, что аргумент будет в допустимом состоянии после вызова функции (и никакие операции перемещения выполняться не будут).
смотрите также Учебник по C++ для более подробной информации о семантике перемещения, а также станд::переместить .
Вы можете использовать ссылки rvalue для объявления пар перегружаемых функций, одна из которых имеет Фу&& , еще один с константа Foo& .
Обычно программисты предпочитают передавать аргументы по значению.
Однако использование нескольких функций может повысить производительность или позволить универсальному коду поддерживать большое количество типов.
И не забывайте: если вы пишете более сложный код ради производительности, проверьте, действительно ли это помогает. Вы можете использовать «ссылки на пересылку» вместе с станд:: вперед для реализации «идеальной пересылки».
Дружественные сущности
В некоторых случаях допустимо использовать классы и функции в качестве друг .Дружественные типы обычно определяются в одном и том же файле, поэтому нет необходимости открывать другой файл, чтобы понять использование частных членов класса.
Общего пользования друг : когда занятия ФуБилдер объявлен дружественным по отношению к классу Фу , Так ФуБилдер может правильно настроить внутреннее состояние Фу без необходимости раскрывать это состояние всем остальным.
В некоторых случаях удобно сделать класс модульного теста дружественным к исходному классу.
Дружелюбие расширяет (но не нарушает) инкапсуляцию классов.
В некоторых случаях, когда вам нужно предоставить доступ к внутреннему состоянию только одному классу, лучше объявить его другом, чем делать дополнительные публичные члены класса.
Однако в противном случае классы должны взаимодействовать только через публичные функции.
Исключения (программное обеспечение)
Мы НЕ используем исключения C++.Позади
- Исключения позволяют обрабатывать ситуации типа «это невозможно» не в том месте, где произошла ошибка, а на более высоком уровне, позже.
И все это без копания в источниках и составления таблиц с кодами ошибок.
- Исключения используются в большинстве современных языков программирования, а их использование в C++ позволяет писать код, концептуально схожий с Python, Java и т. д.
- Некоторые библиотеки C++ используют в своей работе исключения, и отказ от них может существенно усложнить интеграцию с этими библиотеками.
- Исключения — единственный способ сообщить о проблемах в конструкторе класса.
Конечно, это можно обойти, используя фабричные методы или В этом() , однако для этого потребуется либо дополнительное выделение памяти, либо, соответственно, обработка специального «недействительного» состояния.
- Исключения очень удобны при использовании в тестировании (в тестовых фреймворках).
- Если используется бросать в функции, то вы должны проверить всех, кто вызывает эту функцию.
Должна быть базовая гарантия безопасности исключений.
Или код никогда не перехватывает исключение, и тогда программа может внезапно завершиться.
Например, есть код, где е() причины г() , что приводит к час() .
Если час выдает исключение, которое перехватывается ж , Что г писать нужно внимательно, иначе могут быть утечки ресурсов и т.д.
- Обычно использование исключений затрудняет отслеживание последовательности выполнения кода.
Например, функции могут завершаться в неожиданных местах.
Это затрудняет поддержку и отладку кода.
Вы можете улучшить ситуацию, следуя (вашим) правилам, когда и где можно использовать исключения.
Однако крайне желательно, чтобы об этих правилах знали другие разработчики.
- Безопасное использование исключений требует использования дополнительных принципов кодирования, таких как RAII. И их количество (принципов кодирования) может быть значительным.
Например, чтобы разработчику не приходилось разбираться в тонкостях всей цепочки обращений к неизвестному ему коду, код сохранения данных в хранилище целесообразно выделить в отдельную фазу фиксации.
У такого выбора могут быть как плюсы, так и минусы (хм, корпоративная политика смешивания кода в одну кучу для удобства затемнения?).
В любом случае использование исключений уменьшает количество доступных опций.
- Использование исключений приводит к разбуханию бинарного файла программы, увеличивает время компиляции (иногда незначительно) и вообще может привести к проблемам с адресным пространством.
- Само наличие исключений может спровоцировать разработчиков выбрасывать их по поводу или без, даже когда в этом нет необходимости, или может привести к трудностям в обработке.
Например, если пользователь вводит недопустимый текст, это не должно приводить к возникновению исключения.
И вообще, если все это описать, то никакого документа не хватит!
Однако для существующего кода введение исключений может повлиять на весь код. Также могут возникнуть проблемы с интерфейсом между новым кодом и старым (без исключений) кодом.
Поскольку большая часть кода C++ в Google не использует исключений, будет очень проблематично внедрить новый код, который будет генерировать исключения.
Существующий код Google не может правильно обрабатывать исключения, поэтому стоимость реализации исключений намного выше, чем реализация любого нового проекта.
Переписывание существующего кода для обработки исключений будет очень медленным процессом с множеством ошибок.
Поэтому лучше использовать альтернативу в виде возврата кода ошибки и утверждения: это не так уж и сложно.
Этот запрет также распространяется на функции, добавленные в C++11, такие как std::Exception_ptr И std::nested_Exception .
Однако для код для Windows есть уступки.
нет, кроме
Пожалуйста, укажите нет, кроме , если это правильно и будет полезно.Определение Спецификатор нет, кроме используется, чтобы указать, что функция не будет генерировать исключения.
Если функция с таким спецификатором все же выдаст исключение, то программа аварийно завершает работу.
станд:: прекратить .
Еще есть оператор нет, кроме .
Он проверяет, заявлено ли выражение как не вызывающее исключений.
Проверка осуществляется на этапе компиляции.
Позади
- Переместите спецификацию конструктора как нет, кроме может улучшить производительность в ряде случаев, например станд::вектор ::изменить размер() будет перемещать объект, а не копировать его, если конструктор перемещения для типа T объявлен как нет, кроме .
- Примечание нет, кроме for функции могут допускать дополнительную оптимизацию, например, компилятор может не генерировать код на основе раскручивания стека.
Конечно, это имеет смысл, если исключения вообще допускаются.
- Если проекты следуют этому руководству и исключения не допускаются, то проверить правильность спецификатора очень сложно.
нет, кроме , если понятие корректности вообще применимо.
- Вы не можете безопасно удалить ранее указанный спецификатор.
нет, кроме , потому что это аннулирует гарантию, на которую может полагаться другой код, причем очень сложными способами.
Считается, что нет, кроме конструктор на ходу может значительно повысить производительность, учтите это.
Если вы ожидаете значительного увеличения производительности от использования нет, кроме по поводу других функций сначала проконсультируйтесь с руководителем проекта.
Используйте безусловный нет, кроме , если исключения полностью запрещены (т. е.
в типичном проекте Google на C++).
В противном случае используйте спецификатор нет, кроме условия (желательно простые), которые становятся ложными в тех редких случаях, когда функция все же может выдать исключение.
В этих тестах могут использоваться проверки характеристик типа (например, std::is_nothrow_move_constructible для объектов, созданных с помощью конструктора перемещения) или распределителей (например, absl::default_allocator_is_nothrow ).
Обратите внимание, что наиболее распространенной причиной исключений является невозможность выделения памяти (и да, мы считаем, что это не относится к конструкторам перемещения — они не должны выдавать исключения из-за ошибок выделения памяти) и существует множество приложений, для которых такая ситуация означает фатальный исход. ошибка, которую даже обрабатывать нет смысла.
И даже в других потенциально ошибочных ситуациях рекомендуется сосредоточиться на простоте интерфейса, а не на поддержке всех скриптов обработки ошибок: например, вместо написания сложного нет, кроме с зависимостью от внешней хэш-функции (независимо от того, генерирует она исключения или нет), вы можете просто задокументировать, что разрабатываемый вами компонент не поддерживает хэш-функции, которые генерируют исключения.
И после этого используйте нет, кроме без каких-либо дополнительных условий.
Информация о типе времени выполнения (RTTI)
Не используйте информацию о типе времени выполнения (RTTI).Определение RTTI позволяет запрашивать информацию о классе объекта C++ во время выполнения.
Выполнено через типизированный идентификатор или динамический_cast .
Позади Типичные альтернативы RTTI (описанные ниже) требуют модификации или перепроектирования иерархии классов, участвующих в запросах.
Иногда такую модификацию сделать очень сложно или нежелательно, особенно в коде, который уже используется в других проектах.
RTTI может быть полезен для модульных тестов.
Например, вы можете проверить фабричные классы на корректность сгенерированного типа.
Это также полезно при построении отношений между объектами и их макетами.
RTTI полезен при работе с абстрактными объектами.
Например:
Против Часто сам запрос типа объекта во время выполнения означает проблемы с дизайном приложения и показывает наличие недостатков в иерархии классов.bool Base::Equal(Base* other) = 0; bool Derived::Equal(Base* other) { Derived* that = dynamic_cast<Derived*>(other); if (that == nullptr) return false; .
}
Неконтролируемое использование RTTI усложняет поддержку кода.
Это может привести к диким условиям, которые зависят от типа объекта и разбросаны по всему коду.
И который придется досконально изучить, если возникнет необходимость что-то изменить в этом коде.
Вердикт Использование RTTI может легко привести к злоупотреблениям, поэтому будьте осторожны.
Постарайтесь ограничить использование RTTI только модульными тестами.
В новом коде рекомендуется отказаться от RTTI. Если вы хотите написать код, который ведет себя по-разному в зависимости от типа объекта, возможно, более подходящими будут следующие альтернативы:
- Виртуальные методы.
Это предпочтительный метод выполнения различного кода в зависимости от типа объекта.
И вся работа (код) выполняется в самом объекте.
- Если ваш код должен находиться вне объектов, вы можете использовать подходы, аналогичные двойной диспетчеризации, например шаблон проектирования «Посетитель».
Это позволит внешнему коду самому определять тип объектов (с помощью системы типов).
Правда, в этом случае лучше использовать static_cast .
Большое количество условий, основанных на типе объекта, является индикатором явных проблем в коде.
if (typeid(*data) == typeid(D1)) {
.
} else if (typeid(*data) == typeid(D2)) { .
} else if (typeid(*data) == typeid(D3)) { .
Подобный код может развалиться при добавлении в иерархию нового дочернего класса.
И вообще, очень сложно модифицировать большое количество разрозненных кусков кода в случае небольших изменений свойств или методов дочерних классов.
И, пожалуйста, не изобретайте свой собственный велосипед, чтобы заменить RTTI. Аргументы против собственного решения будут те же (см.
выше), а разобраться в чужих велосипедах обычно сложнее.
Кастинг
Рекомендуется использовать приведение типов в стиле C++: static_cast (двойное_значение) .Вы также можете использовать инициализацию значения в круглых скобках для преобразования арифметических типов: int64 y = int64{1} << 42 .
Избегайте конструкций типа интервал y = (int)x или интервал у = интервал (х) (хотя последний вариант приемлем при вызове конструктора класса).
Определение C++ расширяет возможности приведения типов за пределы чистого C, добавляя операции приведения.
Позади Основная проблема приведения типов в чистом C — неоднозначность операции.
Например, идентично написанная операция: (интервал)3,5 И (инт) «привет» очень разные по смыслу.
Инициализация скобок и операции в стиле C++ часто помогают избежать такой двусмысленности.
Дополнительный бонус: операции приведения в стиле C++ легче искать в коде.
Против Стиль C++ довольно громоздкий.
Вердикт Избегайте использования приведения типов в чистом стиле C. Вместо этого используйте стиль C++, когда требуется явное преобразование типов.
- Используйте инициализацию в скобках для преобразования арифметических типов ( int64{x} ).
Это самый безопасный способ, потому что.
в других случаях (при потере информации при конвертации) код может не скомпилироваться.
Плюс лаконичный синтаксис.
- Использовать static_cast как эквивалент преобразований в стиле C или когда необходимо преобразовать указатель на дочерний класс в указатель на базовый класс и наоборот (конвертировать указатель на базовый класс в указатель на дочерний класс (и это желательно, чтобы сам объект был экземпляром дочернего класса)).
- Использовать const_cast удалить классификатор константа (см.
константа ).
- Использовать reinterpret_cast для небезопасного преобразования указателей в целые числа или другие типы указателей.
Используйте эту операцию, только если вы знаете, что делаете, и понимаете проблемы выравнивания.
Вы также можете использовать его в таких случаях абсл::bit_cast .
- Использовать абсл::bit_cast для преобразования необработанных битов в другой тип того же размера (например, если вам нужно интерпретировать двойной Как int64 ).
Потоки
Используйте потоки, когда это необходимо, особенно если их использование упрощает ваш код. Перегрузить операцию << только для типов значений и выводить в поток фактические данные (доступные пользователю).Не передавайте внутренние переменные (инварианты и т. д.) и другие детали реализации.
Определение Потоки — это стандартная абстракция ввода-вывода в C++ (см.
стандартный заголовочный файл).
).
Они часто используются в коде Google, особенно для ведения журнала отладки и диагностики.
Позади Операторы << И > > реализуют форматированный ввод-вывод, они просты для понимания, портативны, расширяемы и пригодны для повторного использования.
Противоположностью им является печать , который даже не поддерживает работу с станд::строка .
Кроме того, он не работает с пользовательскими типами и есть проблемы с переносимостью.
Кроме, печать заставляет выбирать среди похожих версий одной функции и перемещаться по десяткам символов формата.
Потоки обеспечивают хорошую поддержку консольного ввода-вывода через станд::cin , станд::cout , станд::церр И станд::засор .
Функции из C API также работают хорошо, но могут потребовать ручной буферизации ввода.
Против
- Форматирование потоков можно настроить через манипуляторы, изменяющие состояние потока.
Обратите внимание, что использование манипуляторов сохраняется с течением времени и, как следствие, поведение кода зависит от истории использования потока (или придется восстанавливать все до известного состояния после каждого случая возможного использования манипуляторов).
).
Кроме того, пользовательский код может (помимо изменения встроенных параметров) создавать свои собственные манипуляторы, добавлять переменные состояния и изменять поведение посредством функции Register_callback.
- Полностью контролировать вывод потока проблематично: изменение настроек (см.
выше), вывод самой мешанины параметров и данных, использование операторной перегрузки (причем компилятор может выбрать не ту перегрузку, которая была задумана) - все это не добавляет управляемость.
- Генерация вывода путем вызова цепочки операторов << усложняет локализацию, поскольку при этом порядок слов жестко фиксирован прямо в коде.
И инструменты поддержки локализации лишены ценности.
- API потоковой передачи может быть сложным и иметь свои тонкости.
И программисты должны уметь с ним работать, чтобы код был эффективным.
- Выбор правильной версии оператора << из большого количества подобных дорого обходится компилятору.
Если они используются часто в большой кодовой базе, время парсинга и анализа может съедать до 20% времени.
Обычно это параметры ввода/вывода в удобочитаемом формате, предназначенные для разработчиков, а не для конечного пользователя.
Также не забывайте, что в проекте уже могут быть устоявшиеся методы ввода/вывода — попробуйте использовать их.
В частности, библиотеки для регистрации и диагностических данных обычно предпочтительнее.
станд::серр или станд::засор .
И вместо станд::строчный поток лучше использовать абсл/строки или их эквивалент. Не рекомендуется использовать потоки для ввода-вывода при обмене данными с конечными пользователями или там, где формат или достоверность данных могут быть поставлены под угрозу.
В таких случаях используйте подходящие библиотеки (шаблоны), которые правильно обрабатывают интернационализацию, локализацию и проверяют правильность данных и формата.
Если вы используете потоки, старайтесь избегать API, которые имеют дело с состояниями, отличными от состояний ошибок.
Те.
не использовать наполнять() , ксалок() И регистр_коллбэк() .
Рекомендуется использовать явные функции форматирования (см.
абсл/строки ) вместо манипуляторов или флагов форматирования для таких вещей, как изменение системы счисления, точность или заполнение нулями до желаемого размера чисел.
Перегрузить оператора << только для типа значения, чтобы оператор создавал удобочитаемое представление.
Не передавайте детали реализации или внутренние переменные.
Если требуется отладочная печать внутреннего состояния, то используйте обычные функции-методы (например, метод класса ОтладочнаяСтрока() - подходящий вариант).
Предварительное приращение и предварительное приращение
Используйте префиксные формы ( ++я ) увеличивает и уменьшает итераторы и другие объекты шаблона.Определение Когда переменная увеличивается ( ++я , я++ ) или уменьшено ( --я , я-- ), а возвращаемое значение не используется, то нужно четко понимать: используйте форму префикса ( ++я , --я ) или постфикс ( я++ , я-- ).
Позади Когда возвращаемое значение игнорируется, префиксная форма не менее эффективна, чем постфиксная форма (обычно префиксная форма более эффективна).
Это связано с тем, что реализация постфикса должна сделать копию переменной с исходным значением, чтобы вернуть результат. Если переменная является итератором или другим сложным типом, то ее копирование может оказаться ресурсоемким.
Итак, если обе формы ведут себя одинаково (возвращаемое значение игнорируется), почему бы не всегда использовать префиксную форму? Против Традиционно в более ранних разработках (особенно на C) использовалась постфиксная форма, особенно для циклов.
для .
Иногда программистам проще читать код с постинкрементом, потому что.
"субъект"( я ) стоит перед словом «действие» ( ++ ), как в английском предложении.
Вердикт Для простых скалярных (необъектных) типов разница в использовании префиксной или постфиксной формы невелика.
Для итераторов и других типов шаблонов используйте форму префикса.
Использование константы
В использовании API константа когда это имеет смысл.В некоторых случаях constexpr будет лучшей альтернативой const. Определение При объявлении переменных или параметров сначала можно указать следующее: константа чтобы показать, что переменные не изменяются (например, константное целое число foo ).
Функции класса могут быть квалифицированы константа чтобы показать, что эта функция не меняет состояние членов класса (например, класс Foo { int Bar(char c) const; }; ).
Позади Облегчает понимание того, как использовать переменные.
Дает компиляторам больше контроля над типами и теоретически генерирует более качественный код. Использование констант обеспечивает дополнительную защиту (уверенность) в корректности кода: функции не могут модифицировать переменные, изменять состояние класса и, как следствие, можно спокойно работать без блокировок в многопоточной среде.
Против Применение константа оно «заразно»: если оно передается константа переменную в функцию, то она должна иметь указание в прототипе константа (или вам придется сделать const_cast ).
И это может быть проблемой при вызове библиотечных функций.
Вердикт Настоятельно рекомендуется использовать константа в API (параметры функций, методы, нелокальные переменные), где это имеет смысл.
Этот подход обеспечивает четкое (и проверяемое компилятором) описание того, как можно изменять объекты.
Четкое различие между модифицирующими (записью) и немодифицирующими (только чтение) операциями очень полезно, особенно для написания поточно-ориентированного кода.
В частности:
- Если функция не меняет аргумент, переданный по ссылке или указателю, то это должна быть ссылка на константу ( константа Т& ) или указатель на константу ( константа Т* ).
- Если параметр передается по значению, используйте константа не дает никакого эффекта.
Поэтому не рекомендуется объявлять постоянный параметр.
смотрите также ТотВ #109 .
- Попробуйте объявить члены класса const ( константа ), если они не меняют логическое состояние объекта (и не позволяют другим что-то менять, например, возвращая неконстантную ссылку).
В противном случае эти методы могут быть небезопасными в многопоточной среде.
Если это не так, то класс должен быть явно объявлен «потокобезопасным».
Константа местоположения
Иногда используется форма int const *foo вместо const int* фу .Это оправдано тем, что первая форма более логична: константа следует за описываемым объектом.
Однако эта «логика» не имеет особого смысла (и обычно не применяется в коде с несколькими вложенными маркерами-указателями), поскольку чаще всего существует только один константа за базовую стоимость.
В этом случае особо заботиться о логике не нужно.
Размещение константа во-первых, делает код более читабельным и совместимым с английским языком: прилагательное ( константа ) стоит перед существительным ( интервал ).
Итак, местоположение константа на первых порах предпочтительнее.
Однако это не строгое условие и если остальной код в проекте использует другой порядок — следуйте коду!
Использование constexpr
Использовать constexpr определить константы или выполнить константную инициализацию.Определение Переменные могут быть объявлены как constexpr чтобы указать на константу, значение которой определяется во время компиляции или компоновки.
Вы также можете объявить функции и конструкторы как constexpr , чтобы их можно было использовать для определения переменной с помощью constexpr .
Позади constexpr позволяет определять выражения с плавающей запятой (помимо литералов), использовать константы для пользовательских типов и вызовов функций.
Против Использование constexpr может вызвать проблемы с обслуживанием (или миграцией) кода, если константность необходимо будет удалить позже.
Текущие ограничения на константные функции или конструкторы могут потребовать реализации дополнительных обходных путей в коде.
Вердикт constexpr позволяет определить неизменяемые части интерфейса.
Использовать constexpr определять константы и функции для присвоения им значений.
Не использовать constexpr , если это требует усложнения кода.
Не использовать constexpr сделать код «встраиваемым».
Целочисленные типы
Среди встроенных целочисленных типов C++ вы должны использовать интервал .Если программе требуется переменная другого размера, используйте целочисленные типы фиксированной длины из , такой как int16_t .
Если в переменной необходимо хранить значения, равные или превышающие 2^31 (2ГиБ), используйте 64-битный тип.
int64_t .
Прикидывая размер, не забывайте, что интервал В расчеты необходимо включать не только результат, но и промежуточные значения.
И, если сомневаетесь, используйте более длинный тип.
Определение C++ не определяет размер целочисленных типов, таких как интервал .
Обычно считается, что короткий содержит 16 бит, интервал — 32, длинный - 32 и долго долго содержит 64 бита.
Позади Объединение в коде.
Против Размеры целочисленных типов в C++ могут различаться в зависимости от компилятора и архитектуры.
Вердикт В определены различные типы: int16_t , uint32_t , int64_t и т. д. Если требуются типы фиксированного размера, не используйте короткий , беззнаковый длинный длинный и тому подобное.
Из целочисленных типов в C только интервал .
Также, где это возможно, используйте size_t И ptrdiff_t .
Тип интервал используется очень часто, особенно для небольших значений, таких как счетчики в циклах.
Вы можете предположить, что интервал содержит минимум 32 бита (но не более).
Если требуется 64-битный целочисленный тип, используйте int64_t или uint64_t .
Для типа, который может хранить «большие значения», используйте int64_t .
Избегайте использования беззнаковых чисел (например, uint32_t ).
Допустимым использованием беззнаковых чисел является использование битовых представлений или использование переполнения (по модулю 2^N) в вычислениях.
Обратите внимание, что также не рекомендуется использовать беззнаковый тип для обозначения отсутствия отрицательных значений: в этом случае используйте утверждения.
Если код возвращает размер контейнера, то убедитесь, что его тип (размер) достаточен для любого Теги: #C++ #перевод с английского #гид по стилю
-
Ид «Коммерсантъ» Поможет Запустить Msn.ru
19 Oct, 24 -
Google Talk С Видео И Голосовым Чатом
19 Oct, 24 -
О Качестве Услуг
19 Oct, 24 -
Инвесторы И Инвестиции В Кремниевой Долине
19 Oct, 24