Upload
sergey-zubkov
View
4.433
Download
0
Embed Size (px)
Citation preview
C++ Core Guidelines
Сергей ЗубковMorgan Stanley
С благодарностями Бьярне Страуструпу
Zubkov - Guidelines - C++ Russia 2017 2
О чем это?
( и почему волнуется Rust?)
Zubkov - Guidelines - C++ Russia 2017 3
Что такое "Современный C++"?• C++11, C++14, C++17, несколько технических
спецификаций (концепции, интервалы, сеть, модули, корутины, и пр), множество новых возможностей и подходов.
Правильный вопрос:
• Как выглядит хороший современный С++?• Как он будет выглядеть в ближайшем будущем?• Как сделать его доступным рядовым программистам, а
не экспертам из комитета?
Zubkov - Guidelines - C++ Russia 2017 4
Что такое "Современный C++"?• Многие программисты (включая экспертов!)• Пишут в устаревшем стиле, игнорируя 20+ лет прогресса и опыта• Пишут в чужеродном стиле (как Java или как C)• Путаются в деталях• Наслаждаются деталями
- Доктор, мне больно, когда я так делаю!- Так не делайте так!
Zubkov - Guidelines - C++ Russia 2017 5
Что такое "Современный C++"?Как сделать современный C++ проще и доступнее?• прямо сейчас!• не создавая новый язык• сохраняя совместимость• cохраняя экспрессивную мощь• сохраняя производительность
Три составные части нашего подхода:Набор правил + Библиотека + Статический анализ
Zubkov - Guidelines - C++ Russia 2017 6
Набор правил + Библиотека + Статический анализ
В каждой компании уже есть набор правил для C++!
Типичные правила:• Создаются в начале проекта/компании
• без опыта и наугад• Устаревают
• обуза для пользователей / технический долг• Узко специализированы
• применяются за пределами специализации• Недостаточно объяснены
• требуют слепого подчинения или эксперта под рукой• Носят запретительный характер
Zubkov - Guidelines - C++ Russia 2017 7
Набор правил + Библиотека + Статический анализ
Чем отличаются Core Guidelines?
• Современный С++ (и С++ ближайшего будущего)• Каким вы хотите видеть новый код через пять лет?
• Предписания, не запрещения• Плюс разъяснения достаточные для использования в обучении
• Не привязаны к конкретной компании• Гитхаб под эгидой isocpp.org• Анализаторы разрабатываются разными компаниями
• Центральные (“Core”)• Компании и проекты могут включать и выключать группы правил (“профили”) и
дописывать/удалять индивидуальные правила по вкусу – и это уже реальность.
Zubkov - Guidelines - C++ Russia 2017 8
Набор правил + Библиотека + Статический анализ
Чем отличаются Core Guidelines?
Не только и не столько стиль программирования, в главные цели входят:• Статическая типобезопасность
• Нет возможности обратиться к T как U
• Безопасность работы с памятью• Нет доступа за пределы массива
• Гарантии времени жизни всех объектов• Нет висячих указателей и ссылок
• Ресурсобезопасность• Нет утечек ресурсов, включая утечки памяти
• В будущем - больше
Zubkov - Guidelines - C++ Russia 2017 9
Набор правил + Библиотека + Статический анализ
Не цели • не минимальные • не ортогональные • не книга (и не учебник) • не руководство по модернизации кода (хотя есть правила модернизации)• не детальный список особых случаев (для этого есть стандарт) • не подмножество языка (не теряет экспрессивность)• не идеальные (сообщайте об ошибках)
Zubkov - Guidelines - C++ Russia 2017 10
Набор правил + Библиотека + Статический анализ
Правил много!• Не все программисты смогут включить все правила
• Как минимум, не сразу: постепенная интеграция жизненно необходима
• Многим потребуется больше правил• Для нетипичных ситуаций
• Наша цель: центральные правила (Core Guidelines)• Правила для большинства
• Переход количества в качество:• Нет утечкам ресурсов• Нет висячим указателям• Нет нарушениям типобезопасности
Zubkov - Guidelines - C++ Russia 2017 11
Набор правил + Библиотека + Статический анализ
“подмножество надмножества”(изначально разработан для доменно-специфичных языков на основе С++)
• Нельзя запрещать без альтернатив• Альтернатива – стандартная библиотека• С дополнениями (потому что “прямо сейчас”)
Guidelines Support Library (https://github.com/Microsoft/GSL)• Для необычных ситуаций – с собственными дополнениями (MyCompany Support Library)• Авторы библиотеки могут пользоваться грязными/сложными/опасными
возможностями языка – для того они и существуют.
Цель: безопасная работа с массивами (без дополнительных проверок!)Надмножество (библиотека): std::string_view, gsl::span, gsl::zstringПодмножество (правила): не превращать массивы в указатели не делать арифметику на указателях не передавать указатель и размер
C++ GSL
Нельзя:
Стандартнаябиблиотека
Можно:
Zubkov - Guidelines - C++ Russia 2017 12
Набор правил + Библиотека + Статический анализ
Microsoft Visual Studio
LLVM Clang
Некоторые другие, близкие по духу, статические анализаторы также планируют включить правила из Code Guidelines (например IFS Cevelop)
Zubkov - Guidelines - C++ Russia 2017 13
Набор правил + Библиотека + Статический анализ
Сколько ошибок в этой программе?
Zubkov - Guidelines - C++ Russia 2017 14
Набор правил + Библиотека + Статический анализ
Zubkov - Guidelines - C++ Russia 2017 15
Набор правил + Библиотека + Статический анализ
Zubkov - Guidelines - C++ Russia 2017 16
Набор правил + Библиотека + Статический анализ
Zubkov - Guidelines - C++ Russia 2017 17
Набор правилgithub.com/isocpp/CppCoreGuidelines
Zubkov - Guidelines - C++ Russia 2017 18
github.com/isocpp/CppCoreGuidelines
• 2014-2015: Morgan Stanley + Microsoft + ЦЕРН• 13 сентября 2015 (CppCon15): на гитхабе
Принимайте участие!
Zubkov - Guidelines - C++ Russia 2017 19
Цели Core Guidelines• Сделать целые классы ошибок невозможными
• Меньше времени в отладчике, меньше шансов для крахов и хаков
• Сделать невозможными утечки ресурсов• Без потери производительности
• Сделать невозможными висячие указатели• Без потери производительности
• Сделать невозможным выход за границы массива• Без потери производительности
• Упростить• Простой код проще поддерживать и проще преподавать• Подальше от темных мест и экзотических приемов
• Программировать как эксперты
Zubkov - Guidelines - C++ Russia 2017 20
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Zubkov - Guidelines - C++ Russia 2017 21
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Смысл кода должен быть понятен из кода:комментарии не компилируются!
change_speed(double s); // Что за double?change_speed(2.3); // В каких единицах?
change_speed(Speed s); // ok: это новая скоростьchange_speed(23_m / 10s); // ok: метры в секундуchange_speed(2.3); // ошибка компиляции
Zubkov - Guidelines - C++ Russia 2017 22
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Пишите на стандартном С++, запрещайте нестандартные расширения языка. Когда они действительно нужны, изолируйте от остального кода.
Для gcc и clang “-std=c++14” недостаточно!Расширения отключаются ”-pedantic-errors”
Zubkov - Guidelines - C++ Russia 2017 23
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Код должен выражать цели, не средства
Плохо:int i = 0;while (i < v.size()) { /* работа с v[i] */ }
Лучше:for (const auto& x : v) { /* работа с x */ } // порядок важенfor_each(par, v, [](int x) { /* работа с x */ }); // порядок неважен
Zubkov - Guidelines - C++ Russia 2017 24
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Статическая типобезопасность(см также type safety profile)
std::variant вместо unionНет преобразований массивов в указатели (см gsl::span)Нет сужающих конверсий (см gsl::narrow_cast)Нет кастов (явных приведений типов)Никаких void*
Zubkov - Guidelines - C++ Russia 2017 25
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Предпочитайте ошибки компиляции проверкам в коде
static_assert(sizeof(void*)==8, “64 bit required”);static_assert(sizeof(VMPage) == PAGESIZE);
f(gsl::span<int>); // а не f(int*, int)
Zubkov - Guidelines - C++ Russia 2017 26
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Если нельзя сделать ошибкой компиляции, должна быть возможность проверить в коде.
Контракты (см gsl_assert)ИсключенияВалидность указателя проверить невозможно!
Zubkov - Guidelines - C++ Russia 2017 27
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Проверяйте ошибки как можно раньше(идеально в конструкторах, чтобы не повторять проверки)
std::vector и gsl::span вместо массивовgsl::not_null вместо указателейСтруктуры и классы вместо строк
Zubkov - Guidelines - C++ Russia 2017 28
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Никаких утечек ресурсов – памяти, файлов, и т п.RAII классы (std::vector, std::fstream, std::lock_guard)std::make_unique/std::make_sharedgsl::finallyНикаких malloc/free/new/delete/strdup/fopen
Zubkov - Guidelines - C++ Russia 2017 29
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Эффективно используйте и память и процессор: это все-таки C++!
Всегда планируйте как минимум возможность оптимизации (Per.7). Избегайте лишнего кода (знайте, что есть в библиотеках), лишних проверок (см P.7 и правила об инвариантах), избыточные структуры данных (от ненужных связных списков до плохо упакованных структур).
Zubkov - Guidelines - C++ Russia 2017 30
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Предпочитайте константы и неизменяемые данные
Все что может быть constexpr или const, должно быть:объекты,методы,ссылки и указатели. (исключения – объекты, возвращаемые из функций)
Zubkov - Guidelines - C++ Russia 2017 31
Секция P: Философия• P.1: Express ideas directly in code• P.2: Write in ISO Standard C++• P.3: Express intent• P.4: Ideally, a program should be statically type safe• P.5: Prefer compile-time checking to run-time checking• P.6: What cannot be checked at compile time should be checkable at run time• P.7: Catch run-time errors early• P.8: Don't leak any resources• P.9: Don't waste time or space• P.10: Prefer immutable data to mutable data• P.11: Encapsulate messy constructs, rather than spreading through the code
Прячьте грязный код в библиотеках за удобными интерфейсами, и больше им не пользуйтесь.Это опять принцип “подмножество надмножества”
в стандартной библиотеке масса примеров: union внутри std::variant, указатели внутри std::vector и пр
Zubkov - Guidelines - C++ Russia 2017 32
Интерфейсы (фрагмент)
• I.1: Make interfaces explicit• I.2: Avoid global variables• I.3: Avoid singletons• I.4: Make interfaces precisely and strongly typed• I.5: State preconditions (if any)• I.7: State postconditions• I.10: Use exceptions to signal a failure to perform a required task• I.11: Never transfer ownership by a raw pointer (T*)• I.12: Declare a pointer that must not be null as not_null• I.13: Do not pass an array as a single pointer• I.24: Avoid adjacent unrelated parameters of the same type• I.26: If you want a cross-compiler ABI, use a C-style subset
Zubkov - Guidelines - C++ Russia 2017 33
Интерфейсы (фрагмент)
• I.1: Make interfaces explicit• I.2: Avoid global variables• I.3: Avoid singletons• I.4: Make interfaces precisely and strongly typed• I.5: State preconditions (if any)• I.7: State postconditions• I.10: Use exceptions to signal a failure to perform a required task• I.11: Never transfer ownership by a raw pointer (T*)• I.12: Declare a pointer that must not be null as not_null• I.13: Do not pass an array as a single pointer• I.24: Avoid adjacent unrelated parameters of the same type• I.26: If you want a cross-compiler ABI, use a C-style subset
Передача массива в виде одного указателя и даже в виде пары аргументов (указатель и размер, указатели на начало и конец) – источник ошибок. Размер массива не должен быть отделен от самого массива.
Плохо: void copy_n(const T* p, T* q, int n); // копия из [p:p+n) в [q:q+n)
Лучше:void copy(gsl::span<const T> r, gsl::span<T> r2); // копия из r в r2
Zubkov - Guidelines - C++ Russia 2017 34
Интерфейсы (фрагмент)
• I.1: Make interfaces explicit• I.2: Avoid global variables• I.3: Avoid singletons• I.4: Make interfaces precisely and strongly typed• I.5: State preconditions (if any)• I.7: State postconditions• I.10: Use exceptions to signal a failure to perform a required task• I.11: Never transfer ownership by a raw pointer (T*)• I.12: Declare a pointer that must not be null as not_null• I.13: Do not pass an array as a single pointer• I.24: Avoid adjacent unrelated parameters of the same type• I.26: If you want a cross-compiler ABI, use a C-style subset
Функции сообщают от ошибках исключениями (“ошибка” – невозможность выполнить свою функцию, гарантировать постусловие или восстановить инвариант).
int printf(const char* ...); // Плохо: возвращает код ошибкиtemplate <class F, class ...Args>explicit thread(F&& f, Args&&... args); // Лучше: бросает system_error
Zubkov - Guidelines - C++ Russia 2017 35
Функции (фрагмент)
• F.22: Use T* or owner<T*> or a smart pointer to designate a single object• F.23: Use a not_null<T> to indicate "null" is not a valid value• F.24: Use a span<T> or a span_p<T> to designate a half-open sequence• F.25: Use a zstring or a not_null<zstring> to designate a C-style string• F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed• F.27: Use a shared_ptr<T> to share ownership• F.44: Return a T& when copy is undesirable and "returning no object" isn't an option• F.45: Don't return a T&&• F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)• F.51: Where there is a choice, prefer default arguments over overloading• F.52: Prefer capturing by reference in lambdas that will be used locally
Zubkov - Guidelines - C++ Russia 2017 36
Функции (фрагмент)
• F.22: Use T* or owner<T*> or a smart pointer to designate a single object• F.23: Use a not_null<T> to indicate "null" is not a valid value• F.24: Use a span<T> or a span_p<T> to designate a half-open sequence• F.25: Use a zstring or a not_null<zstring> to designate a C-style string• F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed• F.27: Use a shared_ptr<T> to share ownership• F.44: Return a T& when copy is undesirable and "returning no object" isn't an option• F.45: Don't return a T&&• F.50: Use a lambda when a function won't do (to capture local variables, or to write a local
function)• F.51: Where there is a choice, prefer default arguments over overloading• F.52: Prefer capturing by reference in lambdas that will be used locally
• gsl::not_null<T> (где T = U*, std::unique_ptr<U>, std::shared_ptr<U>), гарантирует что T - не нулевой указатель. (см также I.12)
• Если параметр not_null, проверка в вызываемой функции не нужна• Eсли результат not_null, проверка в вызывающей функции не нужна
int f(const int* p, gsl::not_null<const int*> q){ if(q) return *q; // бессмысленная проверка (F.23) return *p; // опасный код! Голый указатель может быть нулевым (F.60)}
Zubkov - Guidelines - C++ Russia 2017 37
Функции (фрагмент)
• F.22: Use T* or owner<T*> or a smart pointer to designate a single object• F.23: Use a not_null<T> to indicate "null" is not a valid value• F.24: Use a span<T> or a span_p<T> to designate a half-open sequence• F.25: Use a zstring or a not_null<zstring> to designate a C-style string• F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed• F.27: Use a shared_ptr<T> to share ownership• F.44: Return a T& when copy is undesirable and "returning no object" isn't an option• F.45: Don't return a T&&• F.50: Use a lambda when a function won't do (to capture local variables, or to write a local
function)• F.51: Where there is a choice, prefer default arguments over overloading• F.52: Prefer capturing by reference in lambdas that will be used locally
• Если есть возможность, пользуйтеся аргументами по умолчанию вместо перегрузки функций: перегрузка – для похожих операций над аргументами разных типов.
• Плохо: нет гарантии что код этих двух функций идентиченvoid print(const string& s); // формат по умолчаниюvoid print(const string& s, format f);
• Лучше:void print(const string& s, format f = {});
Zubkov - Guidelines - C++ Russia 2017 38
Параметры и аргументы (фрагмент)
Zubkov - Guidelines - C++ Russia 2017 39
Классы (фрагмент)• C.2: Use class if the class has an invariant; use struct if the data members can vary
independently• C.4: Make a function a member only if it needs direct access to the representation of a class• C.5: Place helper functions in the same namespace as the class they support• C.20: If you can avoid defining any default operations, do• C.21: If you define or =delete any default operation, define or =delete them all• C.35: A base class with a virtual function needs a virtual destructor• C.36: A destructor may not fail• C.40: Define a constructor if a class has an invariant• C.41: A constructor should create a fully initialized object• C.42: If a constructor cannot construct a valid object, throw an exception• C.43: Ensure that a class has a default constructor• C.45: Prefer to use member initializers
Zubkov - Guidelines - C++ Russia 2017 40
Классы (фрагмент)• C.2: Use class if the class has an invariant; use struct if the data members can vary
independently• C.4: Make a function a member only if it needs direct access to the representation of a class• C.5: Place helper functions in the same namespace as the class they support• C.20: If you can avoid defining any default operations, do• C.21: If you define or =delete any default operation, define or =delete them all• C.35: A base class with a virtual function needs a virtual destructor• C.36: A destructor may not fail• C.40: Define a constructor if a class has an invariant• C.41: A constructor should create a fully initialized object• C.42: If a constructor cannot construct a valid object, throw an exception• C.43: Ensure that a class has a default constructor• C.45: Prefer to use member initializers
• Конструктор устанавливает инвариант, функции-члены считают, что инвариант гарантирован.
• Если данные можно менять напрямую, инвариант невозможен. • struct Desc {
string left; // left и right могут меняться независимо друг от друга string right; // инварианта нет};
• class IntArray {public: // конструкторы, деструктор и прprivate: gsl::owner<int*> beg; // инвариант: beg указывает на массив int* end; // инвариант: end указывает на конец массива};
Zubkov - Guidelines - C++ Russia 2017 41
Классы (фрагмент)• C.2: Use class if the class has an invariant; use struct if the data members can vary
independently• C.4: Make a function a member only if it needs direct access to the representation of a class• C.5: Place helper functions in the same namespace as the class they support• C.20: If you can avoid defining any default operations, do• C.21: If you define or =delete any default operation, define or =delete them all• C.35: A base class with a virtual function needs a virtual destructor• C.36: A destructor may not fail• C.40: Define a constructor if a class has an invariant• C.41: A constructor should create a fully initialized object• C.42: If a constructor cannot construct a valid object, throw an exception• C.43: Ensure that a class has a default constructor• C.45: Prefer to use member initializers
• Стандартные конструкторы• Делают возможным массивы и упрощают код• Требуются для все регулярных типов• Предоставляют состояние, которое можно использовать в
операциях перемещения• class Date {
public: Date(Day dd, Month mm, Year yyyy); Date() = default; // См C.45private: int day = 1; int month = 1; int year = 1970;};std::vector<Date> vd(1000);
Zubkov - Guidelines - C++ Russia 2017 42
Наследование (фрагмент)• C.121: If a base class is used as an interface, make it a pure abstract class• C.126: An abstract class typically doesn't need a constructor• C.127: A class with a virtual function should have a virtual or protected destructor• C.128: Virtual functions should specify exactly one of virtual, override, or final• C.131: Avoid trivial getters and setters• C.132: Don't make a function virtual without reason• C.133: Avoid protected data• C.138: Create an overload set for a derived class and its bases with using• C.140: Do not provide different default arguments for a virtual function and an overrider• C.146: Use dynamic_cast where class hierarchy navigation is unavoidable• C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an
error• C.152: Never assign a pointer to an array of derived class objects to a pointer to its base
Zubkov - Guidelines - C++ Russia 2017 43
Наследование (фрагмент)• C.121: If a base class is used as an interface, make it a pure abstract class• C.126: An abstract class typically doesn't need a constructor• C.127: A class with a virtual function should have a virtual or protected destructor• C.128: Virtual functions should specify exactly one of virtual, override, or final• C.131: Avoid trivial getters and setters• C.132: Don't make a function virtual without reason• C.133: Avoid protected data• C.138: Create an overload set for a derived class and its bases with using• C.140: Do not provide different default arguments for a virtual function and an overrider• C.146: Use dynamic_cast where class hierarchy navigation is unavoidable• C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an
error• C.152: Never assign a pointer to an array of derived class objects to a pointer to its base
• Если в классе есть виртуальная функция, деструктор должет быть виртуальными и доступным или невиртуальным и защищенным
• (удаление полиморфного класса с невиртуальным деструктором через базовый указатель – неопределенное поведение)
• struct B { virtual int f() = 0; // деструктор по умолчанию доступный и невиртуальный};struct D : B { string s {"default"}; };void use() { std::unique_ptr<B> p = std::make_unique<D>();} // Undefined Behavior (как минимум утечка памяти из-за строки)
Zubkov - Guidelines - C++ Russia 2017 44
Наследование (фрагмент)• C.121: If a base class is used as an interface, make it a pure abstract class• C.126: An abstract class typically doesn't need a constructor• C.127: A class with a virtual function should have a virtual or protected destructor• C.128: Virtual functions should specify exactly one of virtual, override, or final• C.131: Avoid trivial getters and setters• C.132: Don't make a function virtual without reason• C.133: Avoid protected data• C.138: Create an overload set for a derived class and its bases with using• C.140: Do not provide different default arguments for a virtual function and an overrider• C.146: Use dynamic_cast where class hierarchy navigation is unavoidable• C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an
error• C.152: Never assign a pointer to an array of derived class objects to a pointer to its base
• Тривиальные геттеры и сеттеры не нужны • // Плохо:• class Point {
int x;public: int get_x() const { return x; } void set_x(int xx) { x = xx; }};
• // Лучше• struct Point {
int x = 0;}
Zubkov - Guidelines - C++ Russia 2017 45
Шаблоны (фрагмент)• T.5: Combine generic and OO techniques to amplify their strengths, not their costs• T.61+T.62: Don’t over-parametrize• T.64: Use specialization to provide alternative implementations of class templates• T.65: Use tag dispatch to provide alternative implementations of functions• T.67: Use specialization to provide alternative implementations for irregular types• T.69: Inside a template, don't make an unqualified nonmember function call unless
you intend it to be a customization point• #749 avoid recursion in variadic templates• T.122: Use templates (usually template aliases) to compute types at compile time• T.123: Use constexpr functions to compute values at compile time• T.144: Don't specialize function templates
Zubkov - Guidelines - C++ Russia 2017 46
Шаблоны (фрагмент)• T.5: Combine generic and OO techniques to amplify their strengths, not their costs• T.61+T.62: Don’t over-parametrize• T.64: Use specialization to provide alternative implementations of class templates• T.65: Use tag dispatch to provide alternative implementations of functions• T.67: Use specialization to provide alternative implementations for irregular types• T.69: Inside a template, don't make an unqualified nonmember function call unless
you intend it to be a customization point• #749 avoid recursion in variadic templates• T.122: Use templates (usually template aliases) to compute types at compile time• T.123: Use constexpr functions to compute values at compile time• T.144: Don't specialize function templates
• Излишняя параметризация – источник раздувания кода (даже если компилятор поддерживает ICF).
• Если член шаблона зависит только от N параметров, он должен быть в базовом классе, зависящем только от этих N параметров.
• // Плохо:• template<typename T> class Foo { enum { v1, v2 }; … };• // Лучше:• struct Foo_base { enum { v1, v2 }; … };
template<typename T> class Foo : public Foo_base { … };
Zubkov - Guidelines - C++ Russia 2017 47
Шаблоны (фрагмент)• T.5: Combine generic and OO techniques to amplify their strengths, not their costs• T.61+T.62: Don’t over-parametrize• T.64: Use specialization to provide alternative implementations of class templates• T.65: Use tag dispatch to provide alternative implementations of functions• T.67: Use specialization to provide alternative implementations for irregular types• T.69: Inside a template, don't make an unqualified nonmember function call unless
you intend it to be a customization point• #749 avoid recursion in variadic templates• T.122: Use templates (usually template aliases) to compute types at compile time• T.123: Use constexpr functions to compute values at compile time• T.144: Don't specialize function templates
• Рекурсия в шаблонном коде тормозит компиляцию. Ее можно избежать при помощи fold expressions, constexpr функций, std::index_sequence (std::invoke и т п) и просто pack expansions.
• // Плохо:• template<class T> void pbv(vector<T>& v) {}
template<class T, class H, class... Ts>void pbv(vector<T>& v, H&& h, Ts&&... Ts) { v.push_back(h); pbv(v, ts...);}
• // Лучшеtemplate<class T, class... Ts) void pbv(vector<T>& v, Ts&&... Ts) { (v.push_back(ts), ...);}
Zubkov - Guidelines - C++ Russia 2017 48
Профили• Профиль – набор правил ставящих целью обеспечить конкретную
гарантию.• Type safety (типобезопасность)• Bounds safety (безопасность работы с массивами)• Lifetime safety (гарантии времени жизни объектов)• В будущем – arithmetic, concurrency, noexcept, noalloc, etc.
Zubkov - Guidelines - C++ Russia 2017 49
Профиль типобезопасности• P.4: Ideally, a program should be statically type safe• C.183: Don't use a union for type punning• Type.1: Don't use reinterpret_cast• Type.2: Don't use static_cast downcasts. Use dynamic_cast instead• Type.3: Don't use const_cast to cast away const (i.e., at all)• Type.4: Don't use C-style (T)expression casts that would perform a
static_cast downcast, const_cast, or reinterpret_cast• Type.5: Don't use a variable before it has been initialized• Type.6: Always initialize a member variable
Zubkov - Guidelines - C++ Russia 2017 50
Массивы и Указатели (Bounds Profile)
• ES.55: Avoid the need for range checking• Bounds.1: Don't use pointer arithmetic. Use span instead.• Bounds.2: Only index into arrays using constant expressions.• Bounds.3: No array-to-pointer decay.• Bounds.4: Don't use standard library functions and types that are not
bounds-checked.
Zubkov - Guidelines - C++ Russia 2017 51
Контроль времени жизни объектов (Lifetime Profile)
1. У каждого объекта есть только один владелец2. На объект могут ссылаться много ссылок/указателей3. Никакая ссылка/указатель не может пережить владельца
• Правила для обычной функции:• Не показывать ссылку/указатель на локальную переменную тому кто вызвал• Ссылка/указатель переданная как аргумент может быть возвращена• Указатель от new может быть возвращен если мы сообщим что этот указатель – владелец
int* f(int* p) { int x = 4; return &x; // Ошибка return new int{7}; // OK, только надо отметить что это - владелец return p; // OK: откуда пришло, туда и вернется}
Zubkov - Guidelines - C++ Russia 2017 52
Контроль времени жизни объектов (Lifetime Profile)
• Как отмечать владельцев?• Предпочтительно: std::unique_ptr, std::vector, std::map, std::shared_ptr,• На низком уровне (например внутри std::vector<T>): gsl::owner<T>
template<class T> using owner = T;• Этот низкоуровневый owner существует только на уровне системы типов и статического
анализа, в компилированном коде он неотличим от Tvector<int*> f(int* p){owner<int*> q = new int{7};vector<int*> res = {p, q}; // OK: копия q – не владелецreturn res; // Ошибка: владелец должен быть возвращен или удален}
Zubkov - Guidelines - C++ Russia 2017 53
Правила будущего• T.10/I.9: Specify concepts for all template arguments• T.11: Whenever possible use standard concepts• T.12: Prefer concept names over auto for local variables• T.13: Prefer the shorthand notation for simple, single-type argument concepts• T.20: Avoid "concepts" without meaningful semantics• T.21: Require a complete set of operations for a concept• T.22: Specify axioms for concepts• T.23: Differentiate a refined concept from its more general case by adding new use patterns• T.24: Use tag classes or traits to differentiate concepts that differ only in semantics• T.25: Avoid complementary constraints• T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax• T.30+31: Use !C<T> / C1<T> || C2<T> sparingly • I.6+8: Prefer Expects() for expressing preconditions/ Ensures() for expressing postconditions
См также http://stroustrup.com/good_concepts.pdf
Zubkov - Guidelines - C++ Russia 2017 54
Не-правила и мифы • NR.1: Don't: All declarations should be at the top of a function• NR.2: Don't: Have only a single return-statement in a function• NR.3: Don't: Don't use exceptions• NR.4: Don't: Place each class declaration in its own source file• NR.5: Don't: Don't do substantive work in a constructor; instead use
two-phase initialization• NR.6: Don't: Place all cleanup actions at the end of a function and
goto exit• NR.7: Don't: Make all data members protected
Zubkov - Guidelines - C++ Russia 2017 55
Слишком много правил?• Эти правила для всех:• Новичков, экспертов, библиотек, системщиков, обычных и необычных
приложений
• Никто не предлагает выучить все эти правила!• Никто не предлагает даже прочитать все эти правила!• Подавляющее большинство правил предназначено для
анализаторов:• Анализ отметит возможное нарушение и покажет какое правило
прочитать:•
Zubkov - Guidelines - C++ Russia 2017 56
Мы хотим хороший современный С++
• “Мы” значит “вы” • это не под силу одному человеку или одной компании
• Каким вы хотите видеть код через 5 лет?• “Таким же как и сегодня” – не ответ• Модернизировать много кода – нелегко• Если ясна цель – можно к ней двигаться
• Не все согласны с тем как выглядит хороший С++• Код не должен выглядеть одинакого• Но должно быть общее ядро• Нужно обсуждать и применять правила и анализаторы
• Помогайте!• Нужны правила, анализ, комментарии, редакторы
https://github.com/isocpp/CppCoreGuidelines
Zubkov - Guidelines - C++ Russia 2017 57
Zubkov - Guidelines - C++ Russia 2017 58
extras
Zubkov - Guidelines - C++ Russia 2017 59
Обработка ошибок (фрагмент)
• E.1: Develop an error-handling strategy early in a design• E.2: Throw an exception to signal that a function can't perform its assigned task• E.3: Use exceptions for error handling only• E.5: Let a constructor establish an invariant, and throw if it cannot• E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable• E.13: Never throw while being the direct owner of an object• E.14: Use purpose-designed user-defined types as exceptions (not built-in types)• E.15: Catch exceptions from a hierarchy by reference• E.16: Destructors, deallocation, and swap must never fail• E.17: Don't try to catch every exception in every function• E.18: Minimize the use of explicit try/catch• E.19: Use a final_action object to express cleanup if no suitable resource handle is available
Zubkov - Guidelines - C++ Russia 2017 60
Многозадачность (избранное)• CP.4: Think in terms of tasks, rather than threads• CP.8: Don't try to use volatile for synchronization• CP.20+21: Use RAII, never plain lock()/unlock() / Use std::lock() to acquire multiple mutexes• CP.22: Never call unknown code while holding a lock (e.g., a callback)• CP.23+24: Think of a joining thread as a scoped container / a detached thread as a global container• CP.25+26: Prefer gsl::raii_thread / gsl::detached_thread • CP.31: Pass small amounts of data between threads by value, rather than by reference or pointer• CP.32: To share ownership between unrelated threads use shared_ptr• CP.42: Don't wait without a condition• CP.50: Define a mutex together with the data it protects• Use algorithms that are designed for parallelism, not algorithms with unnecessary dependency on linear
evaluation• CP.60: Use a future to return a value from a concurrent task• CP.100: Don't use lock-free programming unless you absolutely have to
Zubkov - Guidelines - C++ Russia 2017 61
Управление ресурсами• R.1: Manage resources automatically using resource handles and RAII• R.3: A raw pointer (a T*) is non-owning• R.4: A raw reference (a T&) is non-owning• R.5: Prefer scoped objects• R.10+11: Avoid malloc() and free() / Avoid calling new and delete explicitly• R.12: Immediately give the result of an explicit resource allocation to a manager object• R.13: Perform at most one explicit resource allocation in a single expression statement• R.15: Always overload matched allocation/deallocation pairs• R.20: Use unique_ptr or shared_ptr to represent ownership• R.21: Prefer unique_ptr over shared_ptr unless you need to share ownership• R.22+23: Use make_shared() to make shared_ptrs / make_unique() to make unique_ptrs• R.30: Take smart pointers as parameters only to explicitly express lifetime semantics
Zubkov - Guidelines - C++ Russia 2017 62
Выражения и инструкции (избранное)
• ES.5: Keep scopes small• ES.11: Use auto to avoid redundant repetition of type names• ES.20: Always initialize an object• ES.21+22: Don't introduce a variable (or constant) before you need to use it / until you have a value to
initialize it with• ES.23: Prefer the {}-initializer syntax• ES.24: Use a unique_ptr<T> to hold pointers in code that may throw• ES.26: Don't use a variable for two unrelated purposes• ES.28: Use lambdas for complex initialization, especially of const variables• ES.30+31: Don't use macros for program text manipulation / for constants or "functions"• ES.45: Avoid narrowing conversions• ES.48: Avoid casts• ES.56: Avoid std::move() in application code• ES.63: Don't slice
Zubkov - Guidelines - C++ Russia 2017 63
const• P.10: Prefer immutable data to mutable data• ES.25: Declare an object const or constexpr unless you want to modify its value later on• ES.50: Don't cast away const• F.4: If a function may have to be evaluated at compile time, declare it constexpr• R.6: Avoid non-const global variables• Con.1: By default, make objects immutable• Con.2: By default, make member functions const• Con.3: By default, pass pointers and references to consts• Con.4: Use const to define objects with values that do not change after construction• Con.5: Use constexpr for values that can be computed at compile time