# Записи по всем лекциям по C++ Advanced
> [TOC]
# 01 Лекция. Передача параметров по значению, RVO, NRVO
## Передача параметров по значению
При вызове функции, копирование в аргумент происходит снаружи, зделано это для оптимизации, при копировании снаружи - если мы не используем этот аргумент далее - его можно не копировать.
```cpp=
struct test{};
void foo(test val)
{
// NOT here
}
int main()
{
test x();
// copy here
foo(x);
}
```
---
## RVO & NRVO
```cpp=
struct test {};
test foo()
{
test x();
return foo(); /// RVO
return {}; /// RVO
return x; /// NRVO
}
int main()
{
test x = foo();
}
```
Во всех этих случаях гарантированно нет копирования, начиная с С++17 эта оптимизация обязательна (но она и так была с 11)
гарантируется в следующих случаях:
* При return, когда операнд — prvalue того же типа, что возвращаемое значение функции.
* При инициализации объекта, когда инициализатор — prvalue того же типа, что и переменная (без учёта const- и volatile-квалификаторов).
Писать `std::move` в `return` не имеет смысла, тк он только подавит оптимизации. И даже если там не будет оптимизации - гарантированно компилятор туда сам подставит `std::move`!
---
## lifetime extension
(C++03)
При возврате переменных из функции их время жизни будет автоматически продлеваться, и не нужно копирования.
```cpp=
#include <iostream>
int foo()
{
int value = 42;
return value;
}
int main()
{
int& x = foo(); // время жизни переменной value будет продленно
std::cout << x << "\n";
}
```
https://en.cppreference.com/w/cpp/language/lifetime
# 02 Лекция. rvalue-ссылки, move-семантика, xvalue, copy elision, lifetime extension
```cpp
int a; /// value
int &a; /// l-value reference
int &&a; /// r-value reference
```
---
```cpp
int foo();
int& bar();
int&& baz();
int main()
{
decltype(foo()) x; /// prvalue - int x
decltype(bar()) y; /// lvalue - int& y
decltype(baz()) z; /// xvalue - int&& z
}
```
---
```cpp=
int& a = 5;
auto b = &a; /// int&
```
```cpp
& + & = &
& + && = &
&& + & = &
&& + && = &&
```
---
## Values categories

Термины lvalue и rvalue появились ещё в языке C.
Исторически **lvalue** – это то, что может быть слева (left) от оператора присваивания, а **rvalue** – то, что может быть только справа (right).
В C++11 основу новой более строгой терминологии легли 2 свойства:
* наличие идентичности (identity) – т. е. какого-то параметра, по которому можно понять, ссылаются ли два выражения на одну и ту же сущность или нет (например, адрес в памяти);
* возможность перемещения (can be moved from) – поддерживает семантику перемещения.
Обладающие идентичностью выражения обобщены под термином glvalue (generalized values), перемещаемые выражения называются rvalue. Комбинации двух этих свойств определили 3 основные категории выражений:
| | Обладают идентичностью | Лишены идентичности |
| -------- | -------- | -------- |
| Не могут быть перемещены | lvalue | - |
| Могут быть перемещены | xvalue | prvalue |
Источник: https://habr.com/ru/post/441742/
---
---
# 03 Лекция. intrusive контейнеры

# 04 Лекция. shared_ptr
1) `weak_ptr lock` нужен для норм работой с многопоточностью (выдает `shared_ptr` на обьект вместо обычной ссылки)
2) `make_shared` нужен для оптимизации числа аллокаций (`control block` и обьект выделяются вместе)
3) `control block` жив до того момента, пока жив хоть один `weak_ptr` на обьект (и `shared+ptr` естественно)
4) `make_shared` + `weak_ptr` => память под обьект будет занята, пока жив `weak_ptr` (деструтор обьекта будет вызван правильно, когда уничтожиться последний `shared_ptr` на обьект) (смотри пункт 2-3)
---
---
# 05 Лекция. perfect forwarding, universal ссылки, variadic шаблоны
При Perfect Forwarding есть 2 свойства:
1) сохраняет модификаторы (const, ...)
2) сохраняет value categories (move, copy...)
---
```cpp
template <typename T>
void g(T x) /// T будет выводиться без const
```
---
`std::forward` можно полностью заменить на
```cpp
static_cast<T&&>(x)
```
---
`std::remove_reference` внутри `std::forward` нужен только для того, чтобы подавить автоматическое вычисление типа (чтобы программисты не забывали писать тип при вызове `std::forward`)
---
```cpp
template <typename T>
void f(T&& x) {}
```
`T&& x` - универсальная ссылка, тк вычисляется и в l-ref и в r-ref
---
## variadic шаблоны
### множественное наследование
```cpp=
template <typename... U>
struct mytype : U...
{
using U::foo...
};
```
### Как читать ...
**3 варианта с std::forward**
```cpp=
template <typename... Args>
int g(Args&&... args)
{
return foo(std::forward<Args>(args)...);
return foo(
std::forward<Arg0>(arg0),
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2), ...);
//--------------------------------------------------------------------//
return foo(std::forward<Args...>(args...));
return foo(std::forward<Arg0, Arg1, Arg2, ...>(arg0, arg1, arg2, ...));
//--------------------------------------------------------------------//
return foo(std::forward<Args...>(args)...);
return foo(
std::forward<Arg0, Arg1, Arg2, ...>(arg0),
std::forward<Arg0, Arg1, Arg2, ...>(arg1),
std::forward<Arg0, Arg1, Arg2, ...>(arg2), ...);
}
```
**4 примера с функциями**
```cpp=
template <typename... Args>
void zzz(Args... args)
{
f(g(args...));
f(g(arg0, arg1, arg2, ...));
//--------------------------------------------------------------------//
f(g(args)...);
f(g(arg0), g(arg1), g(arg2), ...));
//--------------------------------------------------------------------//
f(g(args, args...)...);
f(
g(arg0, arg0, arg1, arg2, ...),
g(arg1, arg0, arg1, arg2, ...),
g(arg2, arg0, arg1, arg2, ...), ...);
//--------------------------------------------------------------------//
f(g(args, args)...);
f(
g(arg0, arg0),
g(arg1, arg1),
g(arg2, arg2), ...);
}
```
### 2 шаблонных пака в одной функции или структуре
```cpp=
template <typename... Ts>
struct tuple {};
template <typename... U, typename... V>
struct tuple<tuple<U...>, tuple<V...>> {};
template <>
struct tuple<> {};
int main()
{
tuple<tuple<int, int>, tuple<double, double>> t;
}
```
# 06 Лекция. Возврат значения при perfect forwardingе, decltype, auto, nullptr
```cpp
struct test {};
sizeof(test) == 1;
```
[**Источник**](https://www.geeksforgeeks.org/why-is-the-size-of-an-empty-class-not-zero-in-c/#:~:text=The%20size%20of%20an%20empty%20class%20is%20not%20zero.,objects%20will%20have%20different%20addresses.)
---
## decltype
```cpp
int foo();
int& bar();
int&& baz();
int main()
{
decltype(foo()) x; /// prvalue - int x
decltype(bar()) y; /// lvalue - int& y
decltype(baz()) z; /// xvalue - int&& z
}
```
---
## 2 разных decltype
Есть 2 разных `decltype`, для name и value
```cpp
#include <type_traits>
int main()
{
int x;
// x is the name of an entity of type int
static_assert(std::is_same_v<decltype(x), int>);
// (x) is an expression of type int &
static_assert(std::is_same_v<decltype((x)), int &>);
}
```
---
`decltype(auto)` - https://ru.stackoverflow.com/questions/563318/%D0%9A%D0%BE%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D1%8F-decltypeauto
---
## Приколы с возвращаемым типом
Что делать если у функции разные возращаемые значения при разных перегрузках, а вернуть результат хочется:
```cpp=
char f(int&);
std::string f(std::string);
double* f(std::vector<short> const&);
template <template T>
T&& decval() noexept;
template <typename T>
dectype(f(declval<T>())) g(T&& x)
{
return f(std::forward<T>(x));
}
```
`declval<T>()` нужно только для вычисления типа x до его обьявления
Но теперь можно написать нормально (после обьявления функции)
```cpp=
template <typename T>
auto g(T&& x) -> dectype(f(std::forward<T>(x)))
{
return f(std::forward<T>(x));
}
```
Ещё пример со `->`
```cpp=
struct foobar
{
using type = int;
type f();
void g(type);
}
auto foobar::f() -> type {}
auto foobar::g(type x) {}
```
---
perfect_backward, сохраняющий RVO и NRVO
```cpp=
template<int N>
decltype(auto) perfect_backward() {
decltype(auto) result = giver(index_constant<N>{});
printf("%d\n", result);
if constexpr (std::is_reference_v<decltype(result)>) {
return decltype(result)(result);
} else {
return result;
}
}
```
[**Источник**](https://quuxplusone.github.io/blog/2018/09/25/perfect-backwarding/)
---
---
# 07 Лекция. Cтатический и динамический полиморфизм, анонимные функции
Cортировка с функтором быстрее, чем сортировка с указателем на функции (на 30-40%), если же брать уже отсортированные массивы - быстрее в 2.5-3 раза!
```cpp=
bool int_less(int a, int b) /// функция
{
return a < b;
}
template <typename T>
struct less /// функтор
{
bool operator <(T const& a, T const& b)
{
return a < b;
}
}
```
Вызов функцтора в 3-4 раза быстрее, чем функции по указателю.
Вызов `less<int>` **компилятор может встроить** (`inline`), а вызов `int_less` - нет.
---
Есть специальные ключи компилятора `-fprofile-generate` и `-fprofile-use`, которые создают и используют профайл для самых оптимальных настроек компиляции.
---
Лябда функции для написания функции только в данной точке кода (для удобства чтения)
---
`[x, &y, z](){}` - x и z по значению, а y по ссылке
`[=, &y]` - все по значению, а y по ссылке (захватывает this по ссылке)
`[&, x, z]` - все по ссылке, а x и z по значению (захватывает this по ссылке)
`[]` - ничего
`[this]` - все по указателю this
`[*this]` - все по копии текущего класса
`[str = std::move(str)]` - строка помуванная

---
# 08 Лекция.
Возращаемое значение у лябды
```cpp
[]() -> bool {
}
```
---
Неконстантная лябда
```cpp
struct mytype
{
void foo()
{
int n = 0;
[n]() mutable { /// non-const
return ++n;
}
}
}
```
---
Шаблонная лямбда (C++20)
```cpp
struct mytype
{
void foo()
{
int n = 0;
[]<typename U, typename V>(U const& a, V const& b) {
return a < b;
}
}
}
```
---
Написание Шаблонной лямбды (C++17)
```cpp
struct mytype
{
void foo()
{
int n = 0;
[](auto const& a, auto const& b) { /// (auto a, dectype(a) b)
return a < b;
}
}
}
```
---
Новое написание Шаблонной функции (C++20)
```cpp
void foo(auto x) {}
int main()
{
foo<int>(5);
}
```
---
Ещё пример совмещения (`auto` должно идти последним(и))
```cpp
template <typename U>
void foo(U a, auto x) {}
int main()
{
foo<int, float>(5, 5.f);
}
```
---
---
# 09 Лекция.
# 10 Лекция.
---
---
> [TOC]