Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤8👍4🤝2
На картинке — аккуратная “Span Cheat Sheet”, которая показывает, как работать с
std::span: лёгким и не владеющим памятью представлением непрерывной последовательности объектов. Показано, как создавать span из вектора или массива, как получать элементы, размер, поддиапазоны и почему span особенно удобен в параметрах функций.
Сохрани, чтобы больше не путаться с указателями и массивами
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥7❤4
Как добавить “поле-настройку” в struct и не раздувать память?
Иногда хочется хранить рядом с данными “маркер/политику/стратегию”, но сам тип пустой. В обычном виде даже пустой член класса вроде
В C++20 есть атрибут
Сравни:
• было:
• стало:
Это видно через измерение размера:
Во многих ABI
🔥 Это оптимизация, а не гарантия. И ещё нюанс: у поля вроде
📣 C++ Ready | #совет
Иногда хочется хранить рядом с данными “маркер/политику/стратегию”, но сам тип пустой. В обычном виде даже пустой член класса вроде
Tag tag; может занимать место — компилятор часто обязан обеспечивать уникальность адресов и соблюдать выравнивание.В C++20 есть атрибут
[[no_unique_address]], который разрешает компилятору не выделять отдельную память под пустой член.Сравни:
• было:
struct A { int value; Tag tag; };• стало:
struct B { int value; [[no_unique_address]] Tag tag; };Это видно через измерение размера:
std::cout << sizeof(A);
std::cout << sizeof(B);
Во многих ABI
B будет меньше (или не больше), потому что Tag может “слиться” с уже существующим адресом внутри объекта.[[no_unique_address]] Tag tag; адрес &obj.tag может совпасть с &obj, так что не полагайся на уникальность адреса.Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍6🔥4🤝1
Поговорим про std::ssize: размер контейнера со знаком — без кастов и ловушек size_t!
Полезно в обратных циклах и сравнениях индексов, где
Почему так? Потому что
Классическая проблема выглядит так:
Условие
и проверки
🔥
📣 C++ Ready | #практика
Полезно в обратных циклах и сравнениях индексов, где
size_t легко ломает логику.std::vector<int> v{10, 20, 30};
for (int i = std::ssize(v) - 1; i >= 0; --i) {
std::cout << v[i] << ' ';
}Почему так? Потому что
v.size() возвращает size_t, а это беззнаковый тип. Из-за этого даже простые проверки и “обратные” циклы легко превращаются в ловушки: компилятор ругается на сравнение знакового и беззнакового, а иногда логика вообще ломается.Классическая проблема выглядит так:
for (size_t i = v.size() - 1; i >= 0; --i) {
std::cout << v[i] << ' ';
}Условие
i >= 0 для size_t всегда истинно, и цикл может уйти в бесконечность, потому что i никогда не станет “меньше нуля”, он просто переполнится.std::ssize(v) решает это правильно и без кастов. Он возвращает размер контейнера, но уже в знаковом типе, поэтому выражения вродеstd::ssize(v) - 1
и проверки
i >= 0 ведут себя ожидаемо, а предупреждения компилятора исчезают.std::ssize — маленькая привычка, которая убирает целый класс багов вокруг size_t, особенно в циклах и сравнениях индексов.Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍7🔥4
В этой статье:
• Поймёшь, почему «один клиент — один поток» плохо • Соберёшь ThreadPool: очередь задач, воркеры, синхронизация без лишних аллокаций• Построишь конечный автомат: состояния, хендлеры, переходы для HTTP-клиента 🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍3🔥3
Когда в функции несколько
return, а ещё возможны исключения — легко забыть закрыть файл, откатить флаг или завершить “временный режим”. scope_exit решает это просто: задаёшь действие один раз — и оно гарантированно выполнится при выходе из блока.В этом посте:
• Поймём, как работает scope_exit и почему он похож на defer/finally;
• Разберём базовый шаблон “ресурс + уборка рядом” на коротких примерах;
• Посмотрим реальный кейс: мини-профайлер и автоматический rollback.
Освоив
scope_exit, вы сможете делать cleanup в одном месте, не размазывая его по коду, и спокойно добавлять новые выходы из функции без риска “забыть убрать”.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18❤9👍6
Почему после placement new “старый указатель” может начать врать?
В низкоуровневом коде иногда создают объект прямо в заранее выделенной памяти:
Проблема начинается, когда ты вручную заканчиваешь жизнь объекта и создаёшь новый в той же памяти:
и сразу после этого:
Логически кажется: “адрес тот же, значит
После этого чтение через
🔥 Если у тебя обычный прикладной код без ручного управления временем жизни объектов — скорее всего он тебе никогда не понадобится.
📣 C++ Ready | #совет
В низкоуровневом коде иногда создают объект прямо в заранее выделенной памяти:
S* p = new (storage) S(1);. Это placement new — объект живёт в буфере, а не в куче.Проблема начинается, когда ты вручную заканчиваешь жизнь объекта и создаёшь новый в той же памяти:
p->~S();
и сразу после этого:
new (storage) S(2);
Логически кажется: “адрес тот же, значит
p снова указывает на актуальный объект”. Но по правилам C++ это уже другой объект с новой lifetime, и компилятор имеет право оптимизировать так, будто через p ты всё ещё обращаешься к старому (особенно в сложных сценариях внутри шаблонов/контейнеров).std::launder (C++17) — это способ “перепривязать” указатель к новому объекту и дать компилятору понять, что в этой памяти теперь другая сущность:S* q = std::launder(p);
После этого чтение через
q->x считается корректным и не ломается оптимизациями.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍8🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Официальная документация Extra Clang Tools: clang-tidy помогает находить и исправлять типичные ошибки в коде — от нарушений стиля и misuse интерфейсов до багов, которые можно вывести статическим анализом.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤4👍4
std::byte: честные “сырые байты” вместо char/uint8_t!
Он помогает не путать бинарные данные с текстом и не ловить случайную арифметику над “байтами”.
Часто под байты берут
Проблема в том, что такие типы легко начинают жить как “маленькие числа” или “символы”: кто-то делает
В C++17 для этого есть
С ним нельзя случайно делать арифметику, зато можно явно делать побитовые операции.
Как хранить и передавать байты:
Если нужно получить число — делай это явно:
🔥
📣 C++ Ready | #практика
Он помогает не путать бинарные данные с текстом и не ловить случайную арифметику над “байтами”.
Часто под байты берут
uint8_t или char:void send(const std::vector<uint8_t>& data);
int main() {
std::vector<uint8_t> buf = {0x48, 0x65, 0x6C, 0x6C, 0x6F};
send(buf);
}
Проблема в том, что такие типы легко начинают жить как “маленькие числа” или “символы”: кто-то делает
buf[i] + 1, кто-то печатает как текст, и смысл буфера расползается.В C++17 для этого есть
std::byte — отдельный тип именно для бинарщины.С ним нельзя случайно делать арифметику, зато можно явно делать побитовые операции.
Как хранить и передавать байты:
void send(std::span<const std::byte> data);
int main() {
std::vector<std::byte> buf = {
std::byte{0x48}, std::byte{0x65}, std::byte{0x6C},
std::byte{0x6C}, std::byte{0x6F}
};
send(buf);
}
Если нужно получить число — делай это явно:
#include <bit>
std::byte b{0xFF};
int x = std::to_integer<int>(b);
std::byte делает работу с бинарными данными ясной: меньше неявных превращений, меньше путаницы “текст vs байты”, больше контроля.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17❤5👍4👎1
👍11❤5🔥3
Например,
192.168.x.x — для домашней сети, а 10.x.x.x — для крупных корпоративных систем. CIDR (/24, /16, /8) — помогает точно задать размер подсети и количество хостов.На картинке — всё, что нужно знать про IP: диапазоны, маски, специальные адреса, публичные DNS и основы IPv6.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤27👍9🔥8