Top Banner
C++ Core Guidelines Сергей Зубков Morgan Stanley С благодарностями Бьярне Страуструпу
63

C++ Core Guidelines

Apr 12, 2017

Download

Technology

Sergey Zubkov
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: C++ Core Guidelines

C++ Core Guidelines

Сергей ЗубковMorgan Stanley

С благодарностями Бьярне Страуструпу

Page 2: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 2

О чем это?

( и почему волнуется Rust?)

Page 3: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 3

Что такое "Современный C++"?• C++11, C++14, C++17, несколько технических

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

Правильный вопрос:

• Как выглядит хороший современный С++?• Как он будет выглядеть в ближайшем будущем?• Как сделать его доступным рядовым программистам, а

не экспертам из комитета?

Page 4: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 4

Что такое "Современный C++"?• Многие программисты (включая экспертов!)• Пишут в устаревшем стиле, игнорируя 20+ лет прогресса и опыта• Пишут в чужеродном стиле (как Java или как C)• Путаются в деталях• Наслаждаются деталями

- Доктор, мне больно, когда я так делаю!- Так не делайте так!

Page 5: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 5

Что такое "Современный C++"?Как сделать современный C++ проще и доступнее?• прямо сейчас!• не создавая новый язык• сохраняя совместимость• cохраняя экспрессивную мощь• сохраняя производительность

Три составные части нашего подхода:Набор правил + Библиотека + Статический анализ

Page 6: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 6

Набор правил + Библиотека + Статический анализ

В каждой компании уже есть набор правил для C++!

Типичные правила:• Создаются в начале проекта/компании

• без опыта и наугад• Устаревают

• обуза для пользователей / технический долг• Узко специализированы

• применяются за пределами специализации• Недостаточно объяснены

• требуют слепого подчинения или эксперта под рукой• Носят запретительный характер

Page 7: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 7

Набор правил + Библиотека + Статический анализ

Чем отличаются Core Guidelines?

• Современный С++ (и С++ ближайшего будущего)• Каким вы хотите видеть новый код через пять лет?

• Предписания, не запрещения• Плюс разъяснения достаточные для использования в обучении

• Не привязаны к конкретной компании• Гитхаб под эгидой isocpp.org• Анализаторы разрабатываются разными компаниями

• Центральные (“Core”)• Компании и проекты могут включать и выключать группы правил (“профили”) и

дописывать/удалять индивидуальные правила по вкусу – и это уже реальность.

Page 8: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 8

Набор правил + Библиотека + Статический анализ

Чем отличаются Core Guidelines?

Не только и не столько стиль программирования, в главные цели входят:• Статическая типобезопасность

• Нет возможности обратиться к T как U

• Безопасность работы с памятью• Нет доступа за пределы массива

• Гарантии времени жизни всех объектов• Нет висячих указателей и ссылок

• Ресурсобезопасность• Нет утечек ресурсов, включая утечки памяти

• В будущем - больше

Page 9: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 9

Набор правил + Библиотека + Статический анализ

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

Page 10: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 10

Набор правил + Библиотека + Статический анализ

Правил много!• Не все программисты смогут включить все правила

• Как минимум, не сразу: постепенная интеграция жизненно необходима

• Многим потребуется больше правил• Для нетипичных ситуаций

• Наша цель: центральные правила (Core Guidelines)• Правила для большинства

• Переход количества в качество:• Нет утечкам ресурсов• Нет висячим указателям• Нет нарушениям типобезопасности

Page 11: C++ 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

Нельзя:

Стандартнаябиблиотека

Можно:

Page 12: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 12

Набор правил + Библиотека + Статический анализ

Microsoft Visual Studio

LLVM Clang

Некоторые другие, близкие по духу, статические анализаторы также планируют включить правила из Code Guidelines (например IFS Cevelop)

Page 13: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 13

Набор правил + Библиотека + Статический анализ

Сколько ошибок в этой программе?

Page 14: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 14

Набор правил + Библиотека + Статический анализ

Page 15: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 15

Набор правил + Библиотека + Статический анализ

Page 16: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 16

Набор правил + Библиотека + Статический анализ

Page 17: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 17

Набор правилgithub.com/isocpp/CppCoreGuidelines

Page 18: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 18

github.com/isocpp/CppCoreGuidelines

• 2014-2015: Morgan Stanley + Microsoft + ЦЕРН• 13 сентября 2015 (CppCon15): на гитхабе

Принимайте участие!

Page 19: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 19

Цели Core Guidelines• Сделать целые классы ошибок невозможными

• Меньше времени в отладчике, меньше шансов для крахов и хаков

• Сделать невозможными утечки ресурсов• Без потери производительности

• Сделать невозможными висячие указатели• Без потери производительности

• Сделать невозможным выход за границы массива• Без потери производительности

• Упростить• Простой код проще поддерживать и проще преподавать• Подальше от темных мест и экзотических приемов

• Программировать как эксперты

Page 20: C++ 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

Page 21: C++ Core Guidelines

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); // ошибка компиляции

Page 22: C++ Core Guidelines

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”

Page 23: C++ Core Guidelines

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 */ }); // порядок неважен

Page 24: C++ Core Guidelines

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*

Page 25: C++ Core Guidelines

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)

Page 26: C++ Core Guidelines

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)ИсключенияВалидность указателя проверить невозможно!

Page 27: C++ Core Guidelines

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 вместо указателейСтруктуры и классы вместо строк

Page 28: C++ Core Guidelines

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

Page 29: C++ Core Guidelines

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 и правила об инвариантах), избыточные структуры данных (от ненужных связных списков до плохо упакованных структур).

Page 30: C++ Core Guidelines

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, должно быть:объекты,методы,ссылки и указатели. (исключения – объекты, возвращаемые из функций)

Page 31: C++ Core Guidelines

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 и пр

Page 32: C++ Core Guidelines

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

Page 33: C++ Core Guidelines

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

Page 34: C++ Core Guidelines

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

Page 35: C++ Core Guidelines

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

Page 36: C++ Core Guidelines

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)}

Page 37: C++ Core Guidelines

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 = {});

Page 38: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 38

Параметры и аргументы (фрагмент)

Page 39: C++ Core Guidelines

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

Page 40: C++ Core Guidelines

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 указывает на конец массива};

Page 41: C++ Core Guidelines

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);

Page 42: C++ Core Guidelines

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

Page 43: C++ Core Guidelines

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 (как минимум утечка памяти из-за строки)

Page 44: C++ Core Guidelines

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;}

Page 45: C++ Core Guidelines

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

Page 46: C++ Core Guidelines

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 { … };

Page 47: C++ Core Guidelines

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), ...);}

Page 48: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 48

Профили• Профиль – набор правил ставящих целью обеспечить конкретную

гарантию.• Type safety (типобезопасность)• Bounds safety (безопасность работы с массивами)• Lifetime safety (гарантии времени жизни объектов)• В будущем – arithmetic, concurrency, noexcept, noalloc, etc.

Page 49: C++ Core Guidelines

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

Page 50: C++ Core Guidelines

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.

Page 51: C++ Core Guidelines

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: откуда пришло, туда и вернется}

Page 52: C++ Core Guidelines

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; // Ошибка: владелец должен быть возвращен или удален}

Page 53: C++ Core Guidelines

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

Page 54: C++ Core Guidelines

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

Page 55: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 55

Слишком много правил?• Эти правила для всех:• Новичков, экспертов, библиотек, системщиков, обычных и необычных

приложений

• Никто не предлагает выучить все эти правила!• Никто не предлагает даже прочитать все эти правила!• Подавляющее большинство правил предназначено для

анализаторов:• Анализ отметит возможное нарушение и покажет какое правило

прочитать:•

Page 56: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 56

Мы хотим хороший современный С++

• “Мы” значит “вы” • это не под силу одному человеку или одной компании

• Каким вы хотите видеть код через 5 лет?• “Таким же как и сегодня” – не ответ• Модернизировать много кода – нелегко• Если ясна цель – можно к ней двигаться

• Не все согласны с тем как выглядит хороший С++• Код не должен выглядеть одинакого• Но должно быть общее ядро• Нужно обсуждать и применять правила и анализаторы

• Помогайте!• Нужны правила, анализ, комментарии, редакторы

https://github.com/isocpp/CppCoreGuidelines

Page 57: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 57

Page 58: C++ Core Guidelines

Zubkov - Guidelines - C++ Russia 2017 58

extras

Page 59: C++ Core Guidelines

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

Page 60: C++ Core Guidelines

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

Page 61: C++ Core Guidelines

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

Page 62: C++ Core Guidelines

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

Page 63: C++ Core Guidelines

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