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

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

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
Во что выведется тип Т?
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
​​const rvalue reference
#опытным

В прошлом посте мельком упомянул эту конструкцию, а в этом решил раскрыть по-подробнее.

Правые ссылки были введены в С++11 и с тех пор помогают в реализации семантики перемещения. С помощью таких ссылок мы можем убрать ненужное глубокое копирование объектов и внедрить "перемещение" одного объекта в другой. Достигается это с помощью специальных методов: конструктора перемещения и перемещающего оператора присваивания. Выглядит это так:

struct Movable {
Movable(int i) : num{new int(i)} {}
Movable(Movable&& other) {
num = other.num;
other.num = nullptr;
std::cout << "Don't have to copy in ctor\n";
}
Movable& operator=(Movable&& other) {
if (this != &other) {
delete num;
num = other.num;
other.num = nullptr;
std::cout << "Don't have to copy in assignment\n";
}
return *this;
}
~Movable() { delete num;}
int * num = nullptr;
}

Movable obj1{5};
Movable obj2{7};
Movable&& rvalue_ref = std::move(obj1);
Movable obj3{std::move(rvalue_ref)};
obj2 = std::move(obj3);

// OUTPUT
// Don't have to copy in ctor
// Don't have to copy in assignment


Эти два специальных метода всегда имеют такую сигнатуру. Даже если их генерирует за нас компилятор.

То есть по идее, правые ссылки нужны, чтобы перенести из значение текущего объекта в новый объект. И для этого первоначальный объект нужно изменить.

Так за каким хреном нам тогда нужны константный правые ссылки? Чтобы что? С первого взгляда это выглядит так: мы принимаем правые все правые ссылки в перегрузку(неконстантные ссылки биндятся к константным), но все равно копируем объект, потому что ничего другого сделать не можем. Звучит, как бред.

Но все же есть применение у этой конструкции.

Дело в том, что T&& могут кастится к const T&, T&& и const T&&. Наиболее подходящей перегрузкой будет T&&, дальше const T&& и, наконец, const T&. А вот левые ссылки к правым вообще не могут преобразовываться.

Соотвественно, если мы хотим принимать только lvalue в функцию и никак не пропускать правые ссылки, то "Хьюстон, у нас проблема!". Если мы просто определим перегрузку для const T&, то rvalue reference все равно смогут попадать в эту перегрузку. Что нас сильно огорчает.

Однако мы можем совершить ход конем: пометить перегрузку const T&& удаленной. Так как удаленные функции все еще участвуют в разрешении перегрузок, то для T&& более подходящим выбором будет const T&&, нежели const T&. Но мы намеренно удаляем эту перегрузку и тогда компилятор выдает ошибку из-за того, что не может найти подходящий вариант функции.

struct T{};

void f(T&) { std::cout << "lvalue ref\n"; }
void f(const T&) { std::cout << "const lvalue ref\n"; }
void f(const T&&) = delete; //{ std::cout << "const rvalue ref\n"; }

const T g() {
return T{};
}

int main() {
f(g()); // error: use of deleted function 'void f(const T&&)'
f(T{}); // error: use of deleted function 'void f(const T&&)'
}


Это применение и еще парочку других мы рассмотрим на реальных примерах в следующий раз.

Remove obstructing things from your life. Stay cool.

#cppcore #cpp11
👍21🔥76
​​Примеры использования const T&&
#опытным

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

Для чего это может быть нужно?

Допустим, мы храним в поле класса в каком-то виде ссылку на объект. И нам бы очень не хотелось принимать в конструкторе rvalue reference, потому что возможно сразу же после выхода из конструктора для объектов вызовется деструктор и хана этим объектам. И встречаем UB из-за хранения битой ссылки.

Есть такой стандартный класс std::reference_wrapper и его функции помощники std::ref() и std::cref(). Поскольку std::reference_wrapper предполагает хранение ссылки только для lvalue, то стандарт удалил перегрузки std::ref() и std::cref(), которые принимают const T&&.

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;


По той же самой причине такая перегрузка удалена у функции std::as_const, которая формирует левую ссылку на константный тип из аргумента.

template< class T >  
constexpr std::add_const_t<T>& as_const( T& t ) noexcept;


Также константные правые ссылки используются в более сложных штуках, типа std::optional, когда нужно вернуть из него значение.

constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;


С этой же целью оно используется, например, и в std::get.

template< std::size_t I, class... Types >  
constexpr const std::variant_alternative_t<I, std::variant<Types...>>&&
    get( const std::variant<Types...>&& v );

template< class T, class... Types >
constexpr const T&& get( const std::variant<Types...>&& v );


В таких случаях использование const T&& оправдано передачей информации и о ссылочности типа, и о его константности. Это важно в обобщенном программировании, потому что никто не знает с каким типом будет работать шаблонная сущность. Вы вполне можете получить константный временный объект std::optional(да и любого другого объекта), это синтаксически корректно. И чтобы геттер его внутреннего значение отражал свойства обертки, приходится перегружать эти геттеры для любых возможных параметров. Так вот например методы std::optional упомянутые выше вызовутся только для временных константных объектов. И эти свойства отображаются в возвращаемом значении.

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

// MyClass.hpp
class MyClass {
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};

// MyClass.cpp
class MyClass::Pimpl {
public:
int meat {42};
};

MyClass::MyClass() : impl {new Pimpl} { }

MyClass::MyClass(int g_meat) : MyClass() {
impl->meat = g_meat;
}

MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}

MyClass::~MyClass() { delete impl; }

int MyClass::GetMeat() const {
return impl->meat;
}

// main.cpp
int main() {
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
}


Stay useful even if nobody understands you. Stay cool.

#template #cpp11 #STL
11👍8🔥53
​​Отдых

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

Но как этого достигнуть?

Грамотное распределение рабочего времени и перерывов - один из способов.

У вас есть 8 часов рабочего времени. Дада, дорогие программисты. Ни 10 и ни 12. Хардворк энивеа вычеркиваем из списка своих жизненных девизов. Надо работать не тяжело, а с умом. Нам голову создатели придумали не только для того, чтобы в нее есть. Работать долго может только четко выстроенная система.

И в эти 8 часов нужно впихнуть весь обьем насущных задач. Цель - амбициозная и нетривиальная. Но умные дяди придумали некоторые схемы, которые помогут несколько систематизировать этот хаос.

Для начала нужно составлять план на день. Желательно письменно, но можно и в голове, если вы Джимми Нейтроны. Это помогает видеть конкретные цели, при достижении которых мозг выделит дофамин, давая вам приятные ощущения и мотивацию двигаться дальше. Вам же приятно закрывать тикеты в жире? С собственным микропланированием вы можете закрывать несколько задач за день! Сколько приятных эмоций от зачеркивания пунков в своем плане, мммм.

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

Разделяем весь спектр задач на простые, средние и тяжелые. Как только у вас появилось это разбиение, вы делаете не более 1 тяжелой задачи + не более 3-х средних задач + не более 5 легких задач в день. Схема 1+3+5.

Разделение задач по сложности - вполне соответствует реальности среднего рабочего дня. Нужно ответить на почту, создать задачи, провести собес, сходить на митинг и тд. Все это разные по затрате энергии действия и очевидно, что это нужно учитывать при планировании своего дня.

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

Обычно цифры такие: тяжелая задача - 3-4 часа, средняя - 30-40 мин, легкая - 10 мин.

Счетоводы уже все посчитали и напряглись: даже при максимальных цифрах пропадает 1 час. Куда он тратится?

На отдых

Какое бы у вас не было суперское внимание, оно понижается при непрерывной и вовлеченной работе.

Делайте себе перерывы.

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

Но для удаленщиков перерывы не естественны. И парадокс в том, что именно им больше всего они и нужны! Отсутствие естественных отвлекающих факторов приводит к тому, что человек садится за стол в 9 и до вечера прожевывает пятой точкой дырку в этом стуле, зачастую без обеда. Кстати, ставь лайк, если пролеживаешь дырку в диване и зарабатываешь остеохондроз aka работаешь лежа(посмотрим сколько нас).

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

Опять же. Этот пост - не руководство к действию, которое нужно выполнить в строгости. Мы, как и бизнес, должны быть agile и подстраивать свои концепции под ситуацию. Иногда надо и поработать побольше. Иногда, когда чувствуешь, что не вывозишь, отдохнуть побольше.

В общем, вроде простые рекомендации, но внедряя их вы ощущите всю мощь успешного успеха и расти будете не до синьора/лида/Илона Маска, а до самих просторов космоса.

Stay smarter. Stay cool.

#fun #commertial
33👍9🫡7🔥3❤‍🔥2😁2