# Майлстоун. Вы познакомились с основами C++
## Для чего нужны майлстоуны
Майлстоун — это:
- Промежуточный экзамен для проверки, насколько хорошо вы поняли теорию.
- Практика по чтению чужого кода. Очень пригодится на работе.
- Промежуточный итог прохождения курса.
Итак, поехали. В подсказке к каждой задаче вы найдёте не только полезные наводки, но и ссылки на пройденные темы, которые нужно знать для решения.
## Разогрев
Как называется пространство имён стандартной библиотеки C++? Напишите его вместе с оператором разрешения области видимости. {.task_text}
```consoleoutput {.task_source #cpp_chapter_0057_task_0010}
```
Оператор разрешения области видимости: `::`. **Темы, затронутые в задаче:** [пространства имён](/courses/cpp/chapters/cpp_chapter_0053/). {.task_hint}
```cpp {.task_answer}
std::
```
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
В случае необработанного исключения напишите `ex`. {.task_text}
```cpp {.example_for_playground}
import std;
class A
{
public:
A()
{
std::print("a");
throw std::runtime_error("e");
}
void f()
{
std::print("f");
}
};
int main()
{
std::print("1");
try
{
A a;
a.f();
}
catch(const std::exception & e)
{
std::print("x");
}
std::print("2");
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0020}
```
Выполнение кода, бросившего исключение, прерывается. Управление передаётся обработчику исключения. **Темы, затронутые в задаче:** [исключения,](/courses/cpp/chapters/cpp_chapter_0052/) [классы](/courses/cpp/chapters/cpp_chapter_0055/). {.task_hint}
```cpp {.task_answer}
1ax2
```
## Кто соскучился по побитовым операторам?
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
```cpp {.example_for_playground}
import std;
enum Notification
{
Email = 1, // 0b0001
Sms = 2, // 0b0010
Push = 4, // 0b0100
Messenger = 8, // 0b1000
};
int main()
{
const Notification user_settings = static_cast<Notification>(Push | Sms);
const bool send_email = user_settings & Email;
const bool send_sms = user_settings & Sms;
std::println("{} {}", send_email, send_sms);
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0030}
```
Обратите внимание, что значения констант перечисления `Notification` — это степени двойки. При объединении двух значений `Notification` побитовым «ИЛИ» вы получите их комбинацию. Применение к такому числу побитового «И» со значением `Notification` нужно, чтобы проверить, установлено ли интересующее свойство. **Темы, затронутые в задаче:** [перечисления](/courses/cpp/chapters/cpp_chapter_0054/#block-enum) `enum`; [двоичное представление чисел и побитовые операторы](/courses/cpp/chapters/cpp_chapter_0023/) `&` и `|`; [явное приведение типов](/courses/cpp/chapters/cpp_chapter_0011/#block-static-cast) через `static_cast`, [неявное приведение типов.](/courses/cpp/chapters/cpp_chapter_0011/#block-implicit-cast). {.task_hint}
```cpp {.task_answer}
false true
```
В этой задаче применяется трюк для упаковки нескольких флагов в одну переменную. Причём в нашем случае у переменной тип — перечисление, а его значения равняются степеням двойки: `0b0001`, `0b0010`, `0b0100`, `0b1000`. Значит, их можно объединять побитовым «ИЛИ», чтобы устанавливать несколько флагов: `Push | Sms`. А чтобы проверить, установлен ли конкретный флаг, к значению нужно применить побитовое «И» с константой из перечисления: `user_settings & Sms`.
Упаковка множества флагов в одно число практикуется и для хранения данных в БД. Чем больше записей в таблице и чем критичнее время доступа, тем больший выигрыш вы получите от этого подхода. Представьте, что у вас таблица с десятками миллионов записей. При добавлении в нее нового поля типа `bool` придется проводить миграцию. Если же вместо нескольких полей типа `bool` в таблице выделено целочисленное поле под флаги, миграция не потребуется. Вы просто начнёте использовать не занятый бит числа.
## Это какой-то неправильный калькулятор
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
В случае исключения напишите `ex`. {.task_text}
```cpp {.example_for_playground}
import std;
int main()
{
const char op = '/';
int a = 8;
int b = 3;
switch(op)
{
case '*':
a *= b;
case '/':
a /= b;
case '-':
a-=b;
break;
case '+':
a+=b;
break;
default:
throw std::runtime_error("Wrong operator");
}
std::println("{}", a);
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0040}
```
Если в блоке `case` отсутствует `break`, то выполнение переходит к следующему `case`. **Темы, затронутые в задаче:** [конструкция](/courses/cpp/chapters/cpp_chapter_0032/) `switch-case`; [операторы составного присваивания.](/courses/cpp/chapters/cpp_chapter_0022/#block-compound-assignment) {.task_hint}
```cpp {.task_answer}
-1
```
Мы уже предупреждали, что забытый `break` — это самая популярная ошибка при использовании `switch-case`.
## Шаблоны и линейная интерполяция
Линейная интерполяция (lerp, linear interpolation) используется в геймдеве, 3D-графике и других областях для плавного перехода между двумя значениями. Например, между цветами, координатами или громкостью. {.task_text}
Функция `lerp()` находит промежуточное значение между точками `start` и `end` на прямой с использованием коэффициента `t`, который обычно принимает значения от 0 до 1. {.task_text}
**Какой тип** у значения, возвращаемого при вызове `lerp()` внутри `main()`? {.task_text}
В случае ошибки компиляции вместо типа напишите `err`. {.task_text}
```cpp {.example_for_playground}
import std;
template <class T, class P>
T lerp(T start, T end, P t)
{
return start + t * (end - start);
}
int main()
{
std::println("{}", lerp(-1, 1, 0.5));
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0050}
```
Компилятор найдёт вызов шаблонной функции `lerp()` и для данного набора аргументов инстанцирует шаблон: создаст функцию с типами аргументов `int`, `int`, `double` и типом возвращаемого значения, совпадающим с типами двух первых аргументов. **Темы, затронутые в задаче:** [шаблоны.](/courses/cpp/chapters/cpp_chapter_0056/) {.task_hint}
```cpp {.task_answer}
int
```
Шаблонный код в C++ может выглядеть запутанным. Но чем чаще вы будете практиковаться читать чужой код с шаблонами и писать собственный, тем больше вам будут нравиться шаблоны.
## Ох уж эти вопросы с собесов про инкремент и декремент!
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
```cpp {.example_for_playground}
import std;
int main()
{
std::size_t a = 8;
std::size_t b = 9;
std::size_t x = ++a;
std::size_t y = b++;
std::println("{}", x, y);
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0060}
```
Префиксный оператор возвращает обновлённое значение переменной, а постфиксный — старое. **Темы, затронутые в задаче:** [пре-инкремент и пост-инкремент.](/courses/cpp/chapters/cpp_chapter_0022/#block-increment) {.task_hint}
```cpp {.task_answer}
9 9
```
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
```cpp {.example_for_playground}
import std;
int main()
{
int a = 4;
int b = 8;
int res = a > b ? a++ : ++b;
std::println("{} {}", a, b);
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0070}
```
Как и конструкция `if-else` тернарный оператор гарантирует, что вычисляется только одна из веток условия. Префиксный оператор возвращает обновлённое значение переменной, а постфиксный — старое. **Темы, затронутые в задаче:** [пре-инкремент и пост-инкремент,](/courses/cpp/chapters/cpp_chapter_0022/#block-increment) [тернарный оператор.](/courses/cpp/chapters/cpp_chapter_0031/#block-ternary-operator) {.task_hint}
```cpp {.task_answer}
4 9
```
Что выведет этот код? {.task_text}
В случае ошибки компиляции напишите `err`. {.task_text}
```cpp {.example_for_playground}
import std;
int main()
{
std::size_t a = 0;
std::size_t b = 0;
if (a++ && ++b)
std::println("1 {} {}", a, b);
else
std::println("2 {} {}", a, b);
}
```
```consoleoutput {.task_source #cpp_chapter_0057_task_0080}
```
В C++ логические операторы выполняются по короткой схеме. **Темы, затронутые в задаче:** [пре-инкремент и пост-инкремент,](/courses/cpp/chapters/cpp_chapter_0022/#block-increment) вычисление логических операторов [по короткой схеме.](/courses/cpp/chapters/cpp_chapter_0022/#block-short-circuit) {.task_hint}
```cpp {.task_answer}
2 1 0
```
Чтобы легко отвечать на вопросы про операторы `++` и `--`, нужно помнить три вещи:
- Приоритет постфиксных форм инкремента и декремента выше, чем префиксных.
- Префиксный оператор возвращает обновлённое значение переменной, а постфиксный — старое.
- Префиксные формы возвращают саму переменную, а постфиксные — её неизменяемую копию.
Если эти операторы участвуют в логическом выражении, то помните, что в C++ реализована [стратегия вычисления по короткой схеме](/courses/cpp/chapters/cpp_chapter_0022/#block-short-circuit) (short-circuit evaluation).
----------
## Что дальше
Если вы легко преодолели майлстоун — это отличная причина ~~похвалить себя~~ пожаловаться нам, что задачи слишком лёгкие. Если же с какими-то задачами справиться не получилось, не сдавайтесь. Просто перечитайте перечисленные в подсказках разделы глав и попробуйте снова.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!