Грокаем C++
9.37K subscribers
44 photos
1 video
3 files
574 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам (+ реклама) @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
​​ParamType - универсальная ссылка
#опытным

Один и самых интересных, сложных, непонятных и противоречивых кейсов в выводе шаблонных параметров. Да и интересный он скорее из-за всего остального.

Только при такой сигнатуре шаблонной функции можно считать ее параметр универсальной ссылкой:

template <class T>
void func(T&& param) {...}

func(expression);


То есть это rvalue reference на cv-неквалифицированный тип. Только в таком виде тип param называется универсальной ссылкой. Как говорят в школе:
И ни в каком другом виде!

Ни
template <class T>
void func(std::vector<T>&& param) {...}

Это просто rvalue reference.
Ни
template <class T>
void func(const T&& param) {...}
Это тоже просто rvalue reference! Только константный.
И к последним двум кейсам применяются правила
отсюда
 и 
отсюда.


Когда expression - rvalue reference, то Т выводится безссылочным типом, чтобы тип ParamType был rvalue reference of T. Если тип expression - lvalue, то Т выводится в тип lvalue reference. Самое интересное, что это единственный кейс, когда тип Т выводится в ссылку.

Есть такое правило, что & + && = &. То есть при использовании универсальной ссылки в параметре шаблонной функции при передаче туда lvalue|lvalue reference, этот параметр выводится в lvalue reference. Это происходит именно за счет того, что шаблонный тип выводится в тип lvalue reference. Условно: функция принимает Т && , T выводится в int&, подставляем Т в параметр функции и получаем int& &&. Но такого синтаксиса нет и 2 ссылки коллапсируют в одну левую ссылку int&.


template<typename T> void f(T&& param); // param is a universal reference

int x = 27;
const int cx = x;
const int& lrx = x;
int&& rrx = 42;

f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(lrx); // lrx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is prvalue, so T is int, param's type is therefore int&&
f(std::move(rrx)); // rrx is xvalue, so T is int, param's type is therefore int&&


Обратите внимание на первые 3 кейса. Там Т выводится в lvalue reference тип. В двух последних Т - просто int безо всяких ссылок.

Мы на самом деле уже обсуждали универсальные ссылки в рамках серии статей про категории выражения. Вот ссылочка на эту статью с более полным описанием процессов.

В этой статье я просто хотел подсветить самые важные моменты в этой теме, которые касаются именно вывода типов.

Stay universal. Stay cool.

#cppcore #cpp11 #template
👍10🔥82
Квизы

Сейчас и завтра пойдет пачка опросов по теме вывода шаблонных типов, чтобы вы могли проявить свои знания и проверить их на практике. Идею предложил Антон в своем комменте. Много постов опросников - это конечно не онлайн тренажер, но зато просто и легко в реализации. И каждый сможет попробовать.

Не будет драконьих конструкций, только все то, что мы уже знаем и разбирали на канале.

Не буду использовать телеграммные квизы с ответами, мне кажется это менее интерактивным форматом. Через пару часиков скину скопом объяснения по каждому случаю

У меня к вам всего один вопрос.

Во что выведется тип Т?

#quiz
8👍2🔥2
Во что выведется тип Т?
Anonymous Poll
41%
int
28%
int&
12%
int&&
19%
Будет ошибка компиляции
Объяснение

Кратко пройдемся по каждому кейсу и разберемся, что к чему.

template <class T>
void func(const std::deque<T>& param) {}

func(std::deque<const int>{});


Здесь казалось бы по всем правилам и канонам тип Т должен выводится в const int. Но мы с вами уже знаем, что стандартные контейнеры не могут быть инстанцированы с константными типами, поэтому здесь будет ошибка компиляции.

template <class T>
void func(const std::shared_ptr<T> & ptr) {}

std::shared_ptr<const int> ptr{};
const std::shared_ptr<const int>& ref_ptr = ptr;
func(ref_ptr);


В этом случае правильным ответом будет Т - const int. Убираем от типа ref_ptr константность и ссылочность, снимаем слой std::shared_ptr и остается const int. Довольно просто.

template <class T>
struct Class {
static void func(T&& param) {}
};

int a = 0;
Class<int>::func(a);


Все выглядит так, что param - универсальная ссылка. Однако, это не так. Универсальная ссылка имеет вид Т&&, но с оговоркой, что Т - шаблонный параметр функции/метода, а не класса. В этом случае func принимает просто rvalue reference, а мы передаем туда lvalue. Компилятор не сможет забиндить rvalue reference на lvalue и произойдет ошибка компиляции.

template <class T>
void func(T * ptr) {}

int a = 0;
int const * const ptr = &a;
func(ptr);


Для начала, пусть вас не беспокоит, что const стоит после int, но перед символом указателя. Так можно делать, это просто альтернативная нотация для объявления константных типов.

То что изначальная переменная а имеет тип int нас не должно волновать, мы смотрим только на ptr.

Указатель здесь копируется по значению, поэтому внешняя константность типа ptr отбрасывается при выводе типа. Снимает слой константности и получает const int.

Вот такой формат закрепления материала. Завтра в любом случае выйдет похожий квиз, но если хотите больше такого взаимодействия - ставьте шампусик.

Check your knowledge. Stay cool.
🍾63👍11🔥43🤯3🤬1
Объяснение

Пойдем по порядку

template <class T>
void func(std::vector<T> && param) {}

std::vector<int> vec;
func(vec);


Снова проверка на универсальную ссылку. Только в формате Т&& параметр шаблонной функции может называться универсальной ссылкой. Здесь такого нет.

Мы передает обычный lvalue в функцию, оно не сможет кастануться к rvalue reference, поэтому будет ошибка компиляции.

template <class T>
void vector(const T & param) {}

vector({1, 2, 3});


Интересный случай. Потому что мы все знаем приколы с конструкторами вектора, и их стремлению интерпретировать набор объектов внутри фигурных скобок как std::initializer_list. Я даже назвал функцию vector, чтобы вас надурить немного. Да и по правилам вывода типов auto, {1, 2, 3} тоже выведется в std::initializer_list. Но вывод в шаблонах - это другое. Компилятор не может правильно интерпретировать, что за сущность мы пытаемся засунуть в функцию, поэтому откажется это компилировать.

template <class T>
void func(std::shared_ptr<const T> & ptr) {}

func(std::shared_ptr<const int>{});


Вроде как должно быть int, но если внимательно присмотреться к сигнатуре функции, то можно рассмотреть, что она принимает неконстантную ссылку. А временный объект, который мы в нее передаем, не сможет привестить к неконстантной ссылке. Поэтому снова будет ошибка компиляции.

template <class T>
void func(T&& param) {}

const std::vector<int> vec;
func(vec);


Нет, здесь не будет ошибки компиляции. Слишком много уловок - нехорошо. Тут просто хороший кейс на проверку полноты усвоенного материала. Здесь param - универсальная ссылка. Передаем мы в функцию lvalue, а это значит, что тип Т точно будет ссылкой. Вопрос на что. Здесь никакой вложенности нет, поэтому ничего от типаvec не убираем, а только добавляем сслочность. Ответ - const std::vector<int>&.

Check your knowledge. Stay cool.
🔥24👍1121❤‍🔥1
​​Отпуск

Ребята, Грокаем С++ в отпуске! Посты будут выходить чуть реже, но все же будут и там запланированы несколько интересных тем.

Профессиональная карьера - это не только про непрерывную работу с 9 до 18, и вечер, занятый пилением пет-проектов. Имхо, но настоящий профессионал - человек, который знает, как грамотно отдыхать. И не имею ввиду просиживать штаны на работе.

Знаю кучу историй, когда люди не ходили в отпуск годами. Когда компания заставляла брать отпуск, а ребята брали его в выходные. Лишь бы ничего не пропустить. "А вдруг прод решит неожиданно заболеть?" Всегда надо быть на подхвате. "Да и какой отпуск вообще, С++ - это зизнь!!"

Что мы имеем в итоге? Человеку 30-40 лет, мир не видел, системные выгорания из-за переработок, перепады настроения, радикулит жопы из-за сидячей работы и дряблое тело из-за отсутствия активности. You name it.

Стратегия постоянного впахивания, конечно, имеет место быть и дает свои плоды. По-началу надо хреначить, чтобы выбиться из студенческой бедности и остальной серой массы, наконец устроиться на работу и начать приносить пользу.

Как только у вас есть приличное жилье и хорошая еда - давайте себе отдых. На самом деле все просто: загружены мозги? Не думай!

Сидеть в телефоне и смотреть видосики не катит. Мозг не отдыхает. Он продолжает анализировать большой объем информации. По итогу вы и не отдохнули, и ничего полезного не сделали.

На мой взгляд, есть 2 самых эффективных способа не думать: физическая работа и яркие впечатления.

80% всех наших клеток мозга - моторные нейроны, которые отвечают за движение и координацию
. И только лишь небольшая часть отвечает за мыслительный процесс. Исследования четко показывают - занятие физическими нагрузками имеет долгосрочное позитивное влияние на когнитивные способности. Потому поход в зал - это ваши вложения не только в свое тело, но и в длительную и продуктивную карьеру.

Ну и яркие впечатления. Если вы посмотрите в ретроспективе на свою жизнь, то большинство ваших воспоминаний - какие-то яркие моменты из прошлого. Первое сальто с крыши гаража в снег, прыжок с парашютом, рождение ребенка - вот из чего состоит наша память. Эмоции так переливаются через край, что существуете только вы, этот момент и больше ничего. Ни тасок, ни дедлайнов, ни назойливых менеджеров. Такие события дают большой буст в жизненной энергии. Но не только. По итогу все рано или поздно понимают, что ради этих событий и стоит жить.

Отдыхайте, друзья. В будущем, скажете себе спасибо.

Be a professional. Stay cool.

#fun #commercial
🫡50🍾24❤‍🔥10🔥53🤓2