Мы прям недавно обсуждали, что std::invoke позволяет вызвать указатель на метод класса, это у вас не должно было вызвать вопросов. Самая загвоздка вот тут:
auto fieldPtr = &Data::field;
std::invoke(fieldPtr, Data{});
fieldPtr здесь - это указатель на нестатическое поле класса.
Скорее всего у вас возникли такие вопросы: "Что значит вызвать поле класса?! Это вообще легально?".
И ответ на этот вопрос довольно контринтуитивный. Да, std::invoke помогает единообразно вызвать все похожие на функции сущности. То есть запускать на выполнение код. Но поле класса - это вообще говоря не код, а участок памяти. А указатель на поле класса - это оффсет от начала объекта. На функции это вообще не похоже.
Тем не менее вызов поля класса с помощью std::invoke - легальная операция. И ее результат - значение этого поля.
Странно? Безусловно. Есть ли у этого применения? Конечно!
В C++20 алгоритмах библиотеки ranges есть специальный параметр проекции. Это вызываемая сущность, которая помогает преобразовывать элементы диапазона перед обработкой.
Допустим мы хотим найти в диапазоне объект с максимальным значением определенного поля. Как бы мы это делали без диапазонов:
struct Payment {
double amount;
std::string category;
}
std::vector<Payment>
payments = {{100.0, "food"}, {200.0, "transport"}, {150.0, "food"},
{300.0, "entertainment"}, {50.0, "transport"}, {250.0, "food"},
{120.0, "food"}};
auto max = *std::max_element(
transactions.begin(), transactions.end(),
[](const auto& item1, const auto& item2) { return item1.amount < item2.amount; });Классика: определяем кастомный компаратор для сравнения элементов. Но заметьте сколько кода повторяется. Не легче ли просто один раз указать, что сравнивать надо по полю amount? И библиотека диапазонов позволяет нам это сделать!
auto max = *std::ranges::max_element(payments, {}, [](const auto& elem){return elem.amount;});Последний параметр проекции позволяет нам сказать, как нужно преобразовывать элементы последовательности перед сравнениями.
Но это все еще не идеал. Лямбда здесь кажется оверкиллом. Вот здесь-то вызов поля класса и пригождается:
auto max = *std::ranges::max_element(payments, {}, &Payment::amount);Вот и все. Просто и красиво.
Под капотом алгоритмы рэнджей обязаны использовать std::invoke, чтобы универсально вызывать все переданные коллбэки. Поэтому такой финт ушами работает в них работает. И не работает в привычной STL.
Пользуйтесь диапазонами и проекторами. Это полезные штуки, которые ощутимо упрощают код.
Be laconic. Stay cool.
#cpp20 #STL