Библиотека C/C++ разработчика | cpp, boost, qt
17.2K subscribers
2.2K photos
68 videos
16 files
4.56K links
Все самое полезное для плюсовика и сишника в одном канале.

Как запустить своего ии-агента: https://clc.to/tvpmDQ

По рекламе: @proglib_adv

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5bac324c8ba6dcaa1ad17

#WXSSA
Download Telegram
🔧 std::enable_if_t для проверки иерархии классов

Нужно убедиться, что класс является наследником другого? Проверяйте это на этапе компиляции с помощью type traits.

#include <type_traits>
#include <memory>

class Base {
public:
virtual ~Base() = default;
};

class Derived : public Base {};
class Unrelated {};

// Фабрика, работающая только с наследниками Base
template<typename T>
std::enable_if_t<std::is_base_of_v<Base, T> && !std::is_same_v<Base, T>,
std::unique_ptr<T>>
createObject() {
return std::make_unique<T>();
}

int main() {
auto obj1 = createObject<Derived>(); // OK
// auto obj2 = createObject<Unrelated>(); // Compile error
// auto obj3 = createObject<Base>(); // Compile error
}


Контроль иерархии классов на этапе компиляции
💰 Предотвращение неправильного использования API
⚡️Статическая проверка без runtime-затрат

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
📌 Хранение float в std::map: правильный способ

std::map требует strict weak ordering. float нарушает его при наличии NaN. std::strong_order спасает.

#include <compare>
#include <map>
#include <iostream>

struct FloatCompare {
bool operator()(double a, double b) const {
// strong_order — total order, строго соответствует требованиям map
return std::strong_order(a, b) == std::strong_ordering::less;
}
};

int main() {
std::map<double, std::string, FloatCompare> m;

m[1.0] = "one";
m[-0.0] = "negative zero";
m[+0.0] = "positive zero"; // отдельный ключ! (strong_order различает)
m[std::numeric_limits<double>::quiet_NaN()] = "nan";
m[std::numeric_limits<double>::infinity()] = "inf";

// Все 5 ключей уникальны и упорядочены детерминированно
std::cout << m.size(); // 5
}


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
👍14
🎯 std::move_only_function: зачем нам ещё один тип функций?

std::function — хороший инструмент. Но есть одна проблема: он требует копируемости. А что, если твой callable некопируемый? В C++23 есть решение — std::move_only_function.

Представь: ты хочешь передать лямбду, которая захватывает std::unique_ptr. С std::function — UB или не скомпилируется. С std::move_only_function — просто работает.

// C++23
#include <functional>
#include <memory>

auto make_task(std::unique_ptr<int> data) {
// Работает! std::function здесь не справится
return std::move_only_function<void()>{
[d = std::move(data)]() {
std::println("Value: {}", *d);
}
};
}


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
👍71👾1
💡 __has_include() — это не проверка заголовка, а запрос к препроцессору

Если думал, что __has_include(<vector>) проверяет «существует ли файл на диске». Это не так. Директива работает на уровне препроцессора и взаимодействует с поисковыми путями компилятора, а не с файловой системой напрямую.

🔍 Как это работает

__has_include — это расширение препроцессора, стандартизированное в C++17. Когда компилятор встречает:

#if __has_include(<optional>)
# include <optional>
#endif


— препроцессор проходит по своим include-путям (-I, системные пути, -isystem) и проверяет, разрешится ли имя файла в один из них. Это тот же механизм, что используется при обычном #include, но без реальной вставки содержимого.

🏝 Два синтаксиса — два алгоритма поиска

__has_include(<header>)   // поиск только в системных путях
__has_include("header") // поиск сначала в локальных, затем в системных


Это зеркалит поведение обычных #include <> и #include "". Разница критична при наличии локальных заголовков с теми же именами, что и системные.

❗️ Ловушка: наличие != доступность

Файл может быть найден препроцессором, но при этом не компилироваться на данной платформе. Например, <windows.h> физически присутствует в MinGW, но использование некоторых его частей невозможно без нужного таргета. __has_include вернёт 1, но код всё равно сломается.

‼️ Практический вывод

Используй __has_include для определения наличия необязательных зависимостей, но всегда дополняй проверкой версии или feature-теста (__cpp_lib_optional). Это защитит от ситуации «файл есть, фича недоступна».


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#под_капотом
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤‍🔥41
🧩 Выходной челлендж: дочисти парсер команд

У тебя есть заготовка интерактивной оболочки — read-eval-print loop. Осталось дописать несколько ключевых частей.

#include <iostream>
#include <sstream>
#include <vector>
#include <string>

std::vector<std::string> parseCommand(const std::string& line) {
// TODO: разбить строку на токены по пробелам
// Учти: несколько пробелов подряд — не ошибка
}

void execute(const std::vector<std::string>& tokens) {
if (tokens.empty()) return;

if (tokens[0] == "echo") {
// TODO: вывести все аргументы через пробел
} else if (tokens[0] == "exit") {
exit(0);
} else {
// TODO: вывести "Unknown command: <имя команды>"
}
}

int main() {
std::string line;
while (true) {
std::cout << "> ";
if (!std::getline(std::cin, line)) break;
execute(parseCommand(line));
}
}


Задача: заполни три TODO.

💬 Покажи своё решение — особенно интересны варианты с std::istringstream и ручным разбором.


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчи
ка

#междусобойчик
😁31
👩‍💻 #include хотят переименовать в #please

Комитет ISO пришёл к выводу, что вежливое обращение к компилятору снижает количество ошибок компиляции на 12%.

#please <iostream>
#please <vector>


❗️ Компилятор тоже заслуживает уважения.

Попались? С первым апреля! 😁


📍Навигация:
ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#развлекалово
Please open Telegram to view this post
VIEW IN TELEGRAM
😁39❤‍🔥2😢21
🥳 C++ Ranges. Часть 1: Основы

«Если ты думаешь, что циклы — это нормально, ты просто ещё не видел Ranges»
— Каждый разработчик C++20, примерно через неделю после знакомства с библиотекой


🌸 Введение

Добро пожаловать в мир C++ Ranges — одного из самых мощных нововведений стандарта C++20.

Если ты когда-нибудь писал вот такой код:

std::vector<int> result;
for (const auto& x : data) {
if (x % 2 == 0) {
result.push_back(x * x);
}
}


..то ты знаешь, как это может быть многословно. Ranges позволяют написать то же самое так:

auto result = data
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });


Красиво? Ещё и эффективнее! Давай разберёмся, как это работает.


🍴 Что нам понадобится

Для работы со всеми примерами нужен компилятор с поддержкой C++20 (и лучше C++23):

#include <ranges> // std::views, std::ranges::*
#include <algorithm> // std::ranges::sort и другие


1️⃣ Что такое Range?

Range (диапазон) — это любой тип, у которого есть начало ( begin ) и конец ( end ). Всё, что ты
привык итерировать в цикле for , уже является диапазоном.

std::vector<int> v = {1, 2, 3, 4, 5}; // ✓ Range
std::string s = "hello"; // ✓ Range
int arr[] = {1, 2, 3}; // ✓ Range
std::list<double> l = {1.1, 2.2}; // ✓ Range


С технической точки зрения, тип R является диапазоном, если для него определены
std::ranges::begin(r) и std::ranges::end(r) . В C++ это выражается через концепт:

template<typename R>
concept range = requires(R& r) {
std::ranges::begin(r); // требуем наличие begin
std::ranges::end(r); // требуем наличие end
};



🍪 Категории диапазонов

input_range — однопроходный обход (Пример: поток ввода)
forward_range — многопроходный обход (Пример: std::forward_list)
bidirectional_range — обход в обе стороны (Пример: std::list)
random_access_range — доступ за O(1) по индексу (Пример: std::deque)
contiguous_range — данные в непрерывной памяти (Пример: std::vector , массив)


🐸 Продолжение следует... Дальше разберём, какие алгоритмы можно использовать с Ranges.

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍9🙏2
🍿 C++ Ranges. Views и алгоритмы

Продолжаем тему Ranges в C++.

В библиотеке Ranges есть два главных инструмента работы с последовательностями:

1. std::ranges::* алгоритмы — переработанная версия классической библиотеки <algorithm>. Принимают не пару итераторов, а целый диапазон, поддерживают проекции и лучше взаимодействуют с современными типами.

2. std::views::* адаптеры — ленивые обёртки над диапазонами. Не копируют данные, а описывают как их обойти. Удобны для цепочек преобразований.


🐤 Старый стиль vs новый стиль

std::vector<int> v = {5, 3, 1, 4, 2};

// Старый стиль: передаём пару итераторов
std::sort(v.begin(), v.end());

// Новый стиль: передаём весь диапазон
std::ranges::sort(v);


❗️Разница кажется небольшой, но новый стиль:

• Синтаксически короче
• Поддерживают проекции — сортировку по произвольному полю объекта
• Работают с любым range-совместимым типом, не только с контейнерами


Пример 1. Cортировка

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
std::vector<std::string> names = {"Boris", "Anton", "Victor", "Anna"};

std::ranges::sort(names);

for (const auto& name : names) {
std::cout << name << "\n";
}
// Anna, Anton, Boris, Victor
}



Пример 2. Проекции — сортировка по полю структуры

Одна из главных фич ranges-алгоритмов. Не нужно писать лямбду вручную — достаточно указать, по какому полю сортировать:

#include <algorithm>
#include <vector>
#include <iostream>
#include <string>

struct Person {
std::string name;
int age;
};

int main() {
std::vector<Person> people = {
{"Boris", 30},
{"Anna", 25},
{"Victor", 35}
};

// Сортируем по возрасту, передав &Person::age как проекцию
std::ranges::sort(people, {}, &Person::age);

for (const auto& p : people) {
std::cout << p.name << " " << p.age << "\n";
}
// Anna 25, Boris 30, Victor 35
}



Пример 3. Views — ленивые цепочки

std::views не трогают исходный контейнер и не создают копий — они описывают трансформацию, которая применяется при обходе:

#include <ranges>
#include <vector>
#include <iostream>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};

// Берём только чётные, умножаем на 10, берём первые 3
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * 10; })
| std::views::take(3);

for (int x : result) {
std::cout << x << "\n";
}
// 20, 40, 60
}


❗️Исходный вектор v не изменился. Весь пайплайн вычисляется лениво — только когда мы итерируемся в цикле.


🐸 Продолжение следует... В следующий раз разбираем pipelines

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
Please open Telegram to view this post
VIEW IN TELEGRAM
👍142❤‍🔥1
🩹 C++ Ranges. Конвейеры: оператор |

То, ради чего стоит использовать Ranges — это конвейеры (pipelines). Оператор | позволяет обрабатывать данные через цепочку преобразований:

🥲 Как это выглядело до C++20:

std::vector<int> temp, result;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(temp),
[](int n) { return n % 2 == 0; });
std::transform(temp.begin(), temp.end(), std::back_inserter(result),
[](int n) { return n * n; });


❗️ Вложенные алгоритмы, временные буферы, много шума.


🙂 С конвейером (C++20):

#include <ranges>
#include <vector>
#include <iostream>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Конвейер: фильтруем чётные → возводим в квадрат → берём первые 3
auto result = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);

for (int x : result) {
std::cout << x << " "; // 4 16 36
}
std::cout << "\n";
}


❗️ result — это не вектор, а «вид» (view). Вычисления происходят лениво — только в момент итерации в цикле for. Исходный вектор numbers остаётся нетронутым.

Нужен именно вектор? Материализуйте результат:

// C++23
auto vec = result | std::ranges::to<std::vector>();

// C++20
std::vector<int> vec(result.begin(), result.end());



🐸 Продолжение следует...


📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#константная_правильность
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥75👍2
🔥 Найди баг: копирование строки сломало указатель

#include <string>
#include <iostream>

struct Token {
std::string value;
const char* ptr;

Token(const std::string& s) : value(s), ptr(value.data()) {}
};

int main() {
Token t1("hi");
Token t2 = t1; // копируем

std::cout << t1.ptr << "\n"; // "hi"
std::cout << t2.ptr << "\n"; // ???
}


‼️ Задача: найди баг (если он есть), объясни, почему он связан с SSO, и предложи исправление.

📍Навигация: ВакансииЗадачиСобесы

Библиотека C/C++ разработчика

#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
😁21👍1