TGViewer
Библиотека C/C++ разработчика | cpp, boost, qt Библиотека C/C++ разработчика | cpp, boost, qt @cppproglib · 16.9K subscribers
Post #6759 1.38K
🎱 Проекции: Что такое проекция?

Одна из полезнейших тем в C++ диапазонах — это проекции.

Проекция — это функция, которая «извлекает» ключ из элемента перед тем, как алгоритм с ним работает. Подавляющее большинство алгоритмов из std::ranges принимают необязательный параметр proj; по умолчанию там std::identity — элемент идёт как есть.


ranges::sort — сортировка по полю

Возьмём те самые задачи из прошлого поста:

struct Task { std::string name; int priority; };
std::vector<Task> tasks = { {"Код", 1}, {"Деплой", 3}, {"Тесты", 2} };

std::ranges::sort(tasks, {}, &Task::priority);
// порядок: Код(1), Тесты(2), Деплой(3)


Здесь {} — компаратор по умолчанию (ranges::less), а &Task::priority — проекция. Для каждой пары алгоритм считает less(a.priority, b.priority). Помните, в свёртках, чтобы сложить по priority, пришлось писать лямбду? Тут поле достаёт сама проекция.


🎨 Проекция по указателю на член

Самый частый случай — &Тип::поле. И работает не только в sort:

// найти задачу с priority == 3
auto it = std::ranges::find(tasks, 3, &Task::priority);
// *it == Task{"Деплой", 3}

// посчитать задачи с priority > 1
auto n = std::ranges::count_if(tasks,
[](int p){ return p > 1; }, &Task::priority);
// n == 2


Обратите внимание на порядок: у find проекция идёт сразу после искомого значения (компаратора у него нет), а у count_if — после предиката. У каждого алгоритма свой набор параметров, проекция в нём всегда последняя.


🧁 Проекцией может быть что угодно вызываемое

Не только поле. Подойдёт указатель на метод, лямбда, свободная функция — всё, что переваривает std::invoke:

std::vector<std::string> v = {"apple", "fig", "banana"};
std::ranges::sort(v, {}, &std::string::size); // по длине
// "fig"(3), "apple"(5), "banana"(6)

std::vector<int> nums = {-5, 2, -1, 3};
std::ranges::sort(nums, {}, [](int x){ return std::abs(x); }); // по модулю
// -1, 2, 3, -5



🎁 Проекция меняет ВЗГЛЯД, но не результат

Это главное, что надо уложить в голове. Проекция влияет только на то, ПО ЧЕМУ алгоритм принимает решения. Возвращает он по-прежнему исходные элементы, а не спроецированные значения:

auto top = std::ranges::max(tasks, {}, &Task::priority);
// top — это Task{"Деплой", 3}, а НЕ число 3
// (и это копия: max возвращает элемент по значению)

auto it = std::ranges::min_element(tasks, {}, &Task::priority);
// *it — Task{"Код", 1}, итератор на сам элемент (копии нет)


То есть проекция — это «по какому ключу», а не «во что превратить». Если нужно именно превратить элементы — это уже views::transform.


🍕 У бинарных алгоритмов проекций две

Там, где сравниваются два диапазона (equal, mismatch, …), проекций тоже две — по одной на каждый вход:

std::vector<Task> a = { {"X", 1}, {"Y", 2} };
std::vector<Task> b = { {"A", 1}, {"B", 2} };

// равны ли списки по приоритетам, игнорируя имена?
bool same = std::ranges::equal(a, b, {}, &Task::priority, &Task::priority);
// {} — предикат по умолчанию, дальше proj1 и proj2
// true: 1 == 1, 2 == 2



‼️ Несколько подводных камней

Порядок аргументов. proj идёт после компаратора, а не вместо. sort(tasks, &Task::priority) — указатель на поле не годится в компараторы → простыня ошибок. Правильно: sort(tasks, {}, &Task::priority).
Проекцию зовут много раз. В sort — на каждом сравнении, O(n log n) вызовов. Тяжёлую (парсит строку) выгоднее посчитать заранее и сортировать по готовым ключам.
Проекция ≠ преобразование вывода. На выходе всегда исходные элементы. Превратить их — это views::transform.
Свёртки без проекций. У fold_* параметра proj нет (хотя у for_each и transform есть). «Свернуть по полю» — только лямбдой или через views::transform.


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

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

#константная_правильностьx
  • ❤ 11
More from @cppproglib
  1. Sep 19, 2026А вы уже забрали свой подарок ко Дню программиста? К вашему профессиональному празднику Tp…
  2. Sep 18, 2026Сегодня последний день регистрации на КосмоХакатон! 18–20 сентября, Петербург, Красноярск…
  3. Sep 18, 2026От исходного кода до процессора: разберись, как работает программа 💻 Стартует практически…
  4. Sep 15, 2026😭 Как не потратить недельный лимит AI-кодинга за три дня? Разберём на вебинаре, как трати…
  5. Sep 13, 2026🤪 Если бы можно было задать один вопрос про AI в разработке — что бы вы спросили? Как выб…
  6. Sep 13, 2026🤩 Inline static в классах — забудь про .cpp Сколько раз ты забывал добавить определение s…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →