ключевое слово auto - начиная с C++11 можно объявлять переменные с типом auto. компилятор сам выведет для них тип очевидно, что он вычисляется на этапе компиляции
auto x = expression - тип, который назначается x такой же, какой бы назначался при
template <typename T>
void f(T x) {} - берем по значению, значит, ссылки будут отброшены
...
f(expression);auto работает также, как если бы мы в шаблонную функцию передавали этот экспрешен
как и при выводе типа, если у нас f(T) или auto, то ссылки и верхний уровень CV отбросится:
const int i = 3;
auto i2 = i; - i2 = int
auto без & никогда не &
при T*/T&/T&& и auto&/auto&& ничего не отбросится
где можно писать auto:
- в объявлении переменных (C++11)
- в возвращаемом типе функций (C++11)
- в принимаемом типе функций (С++20)
- в параметрах шаблона (template <auto N>) (C++17)
где нельзя писать auto:
- как шаблонный аргумент: `std::vector
v = {1, 2, 3};
1 инстанцируется шаблон с параметром int, 2 с char
если бы мы хотели ссылку, то мы бы написали
auto& y = x;
это бы работало как если бы в такую функцию передали x
template <typename T>
void f(T& x) {}с * и const аналогично, НО
auto&& z = x; - не CE
template <typename T>
void f(T&& x) {} -- передаем по универсальной ссылкеz - lvalue ссылка на x произошёл reference collapsing
auto&& z = std::move(x); z - rvalue&
range-based for по какому-либо контейнеру
for (auto&& value : std::forward<Container>(container)) - по универсальной ссылке обрабатывать как lvalue, так и rvalue
int* p = new auto(5);
UB - битая ссылка - получили ссылку на временный объект, который был уничтожен при выходе из функции
при возвращаемом типа auto из функции, если типы разные в разных ветках return, то будет CE - inconsistend deduction - не сможет вывести тип работает как вывод шаблона - приведение одного типа к другому не применяется
if constexpr контрит это и подстановка того или иного блока кода происходит раньше, чем вычисление типа auto, поэтому это валидный код
подстановка T -> раскрытие if constexpr -> подстановка auto
лютый пример, где компилятор мог бы уйти в бесконечную рекурсию, пытаясь понять, что такое auto, но на такой случай есть защита - выкинется use of auto g(T) before deduction of auto
auto иногда используется для указания возвращаемого типа функции для того, чтобы сделать trailing return type (С++11)
иногда возвращаемые типы функций длинные и тяжелочитаемые
trailing return type - такой синтаксис, чтобы указать возвращаемый тип после сигнатуры функции, а не до неё
в trailing return type auto ничего не значит, тип именно такой, какой мы укажем
это не вывод типа, это эквивалентно обычной сигнатуре, это просто способ сделать так, чобы читать удобнее было. прост другая форма записи
с C++20 можно писать auto в принимаемом типе функций
void f(auto&& x) {}
//эквивалентно
template <typename T>
void f(T&& x) {}настолько эквивалентно, что можно сделать
f<int>(2)
и void f(auto...) {};
auto работает почти всегда, как вывод шаблона
исключение: initializer_list
auto lst = {1, 2, 3}; - lst будет initializer_list
auto lst = {1, 2, 3.0}; - CE (непонятно init_list от чего)
если в качестве T передавать {1, 2 ,3} - то initializer_list не выведется
у фигурной инициализации нет типа

std::vector<auto> v = {1, 2, 3}; - CE, auto not permitted in template argument
хреновая идея пытаться узнать тип, который вывел шаблон через typeid().name(), потому что typeid обязан выдавать такое имя, будто бы мы пытаемся инициализировать что-то по значению. значит, что ссылки и CV квалификаторы верхнего уровня отбросятся
decltype (C++11) - declared type
decltype - позволяет в compile time узнать тип expression
int x = 0;
decltype(x) y = x;штука, которая в compile time разворачивается в тип того, что в скобках можно подставлять всюду, где ожидается название типа
std::vector<decltype(y)> v;
decltype в отличие от auto не отбрасывает ссылки и CV
int x = 0;
int& y = x;
decltype(y) z = y; - decltype(y) == int&ссылки на переменные практически не отличимы от оригинальных переменных их может отличить только decltype
можно навешивать &,*, &&, const на decltype чтобы сделать const & от decltype, нужно сначала remove_reference потому что
int& x = y;
const decltype(x) z; //int& const == int&
const decltype(&x) p = &x; //int* const
decltype(&x) p = &x //int* constconst при decltype всегда навешивается “над”, а не “под” reference collapsing для навешивания & на decltype также работают
можно брать decltype от экспрешенов
decltype(++x) u = x; - int& u = x;
выведется 1 1
потому что decltype() не вычисляет выражение в скобках, он лишь смотрит на его тип
const decltype(throw 1)* p = &x - const void* p = &x;
decltype работает по разному для переменных и для экспрешенов
- если в скобках у decltype переменная, то он просто вернёт её тип
- если в скобках у decltype экспрешен, то
- если экспрешен - xvalue, то результат будет rvalue& (T&&)
- если lvalue, то lvalue& (T&)
- если prvalue, то T
int x = 0;
decltype(x) y = x; //int y = x
decltype((x)) z = x; //int& z = x;decltype (S().x) z = x; - int z = x
хоть S().x это xvalue, но S().x - это не экспрешен, а энтити, поэтому decltype просто вернет его тип
decltype в возвращаемом типе функций
если бы нам хотелось иметь адаптер над [] от контейнера, где нам бы хотелось возвращать либо ссылку, либо не ссылку (векторбуль) на элемент, чтобы ему присваивать, то такой бы вариант не подошел
template <typename Container>
auto& getElement(Container& cont, size_t index) {
return cont[index];
}
...
std::vector<int> v(5);
getElement(v, 0) = 1;если бы нам хотелось возвращать либо ссылку, либо не ссылку в зависимости от того, какой тип у return, то понадобится decltype в возвращаемом типе можно написать или
template <typename Container>
auto getElement(Container& cont, size_t index)
-> decltype(cont[index]) {
return cont[index];
}но с C++14 есть decltype(auto) decltype(auto) - такой тип, который бы компилятор вывел бы с помощью decltype то есть вывод типа не отбрасывая &
template <typename Container>
decltype(auto) getElement(Container& cont, size_t index) {
return cont[index];
}то есть компилятор самостоятельно выводит тип, исходя из того, что после return по правилам decltype
правило кодстайла: не ставить круглые скобки после return
если функция возвращает auto или decltype(auto), то её нужно определить в этом же файле, иначе компилятор не справится вывести тип
template <typename Container>
decltype(auto) getElement(Container& cont, size_t index) {
return (cont[index]); // теперь здесь опять всегда возвращается ссылка
}
UB, тк возвращается ссылка на локальную переменную (битая ссылка)
она ссылка, потому что (element) - expression
было бы element, то было бы всё нормально
проблема: для возврата разных видом экспрешенов нужно плодить кучу перегрузок функций

решение deducing this (С++23) с C++23 можно принять первый аргумент (себя же) явно. это позволяет сказать нам по какой ссылке мы this принимаем. таким образом мы можем не делать перегрузку методов, которые отличаются видом ссылки объекта this то есть мы будем возвращать в методе константное/неконстантное значение в зависимости от того объект, у которого вызывался этот метод константный/неконстантный используется в std::optional можно использовать для определения метода [] в своих контейнерах
это значит, что мы хотим получить результат такого типа ссылки, по которому мы вызвались таким образом можем сразу определить функцию и для lvalue& и для const lvalue& и для rvalue& и для const rvalue&
теперь можно принять this по значению, а до этого нельзя было, только по ссылке например итератор может быть выгодно принять себя по значению
std::forward_like (C++23) - работает как std::forward, сохраняет вид value, но тип сам подменяет
принимаем тип T&&, но хотим вернуть тип U с такими же свойствами, как T
возвращает 4 случая:
- const U&
- U&
- const U&&
- U&&
эта функция возвращает ссылку на поле такого же вида, как и объект, содержащий это поле
взяли нечто типа T, сделали T с такой же константностью и & как self и decltype от этого - желаемый тип
std::declval<T>() - по данному типу даёт значение такого типа обратная к decltype (по данному значению даёт тип) decltype: объекты -> типы declval: типы -> объекты
CTAD - Class Template Argument Deduction
способность компилятора выводить типы шаблонов не только функций, но и классов
с C++17 можно объявлять объект класса не указывая шаблонные аргументы
std::vector v = {1, 2, 3, 4, 5};
компилятор сам догадается, какой здесь шаблонный аргумент
std::vector v2(5) - CE
нужно, чтобы из параметров конструктора можно определить тип шаблонного класса
std::vector v2{v.begin(), v.end()} - вектор чего? вектор из итераторов на v<int>
потому что скобочки фигурные -> в приоритете конструктор от std::initializer_list
если будут круглые скобочки, то будет вектор интов
у вектора прописаны специальные правила (explicit template deduction guides) как выводить тип своего шаблонного аргумента в ситуации, если подаются 2 одинаковых типа , которые не подошли ни под один конструктор - вектор считает, что это итераторы

с C++20 работает template deduction guide даже для юзингов
template <typename T, typename U>
struct s {
T x;
U y;
}
...
S s{1, 2.0}; // такая агрегатная инициализация с CTAD сработает только с C++20с помощью агрегатной инициализации можно инициализировать не только поля, но и родителей с C++20
до C++20 нужен был такой deduction guide
