Как стать автором
Обновить

Комментарии 16

интересно не с практической но чисто академической точки зрения — грандиозное усложнение кода ради незначительного и весьма ситуациативного бонуса в лаконичности. Кто-то мог бы подумать что это и есть «c++ way», по мне так лучше просто написать лямбду.
Как по мне: излишнее усложнение. Да, у нас будет лаконичнее код, но сколько всего будет требоваться написать или подключить для этого?
Как по мне (я думаю и для других людей), проще будет написать ламбду и двинуться дальше, чем тратить силы на то, чтобы вот такой вот визуальный «хак» применять к коду.
НЛО прилетело и опубликовало эту надпись здесь

isMultipleOf2(x) || isMultipleOf3(x)

НЛО прилетело и опубликовало эту надпись здесь
Пока мир идёт в сторону лаконичных стрелочных лямбд, С++ продолжает поедать кактусы, собирая в одном месте всё больше разных скобочек.
А помоему, при всех этих наворотах, нормальное решение так и не показано…
Что мешает сделать что-то типа (не проверял, может где-то накосячил, тут только идея):
template<class Arg>
std::function<bool(Arg)> operator||(std::function<bool(Arg)> op1, std::function<bool(Arg)> op2)
{
    return [op1, op2](Arg a) {return op1(a) || op2(a);}
}


И тогда можно спокойно использовать тот синтаксис что хотелось в самом начале.
Статью Вы, по всей видимости, не читали, либо С++ для Вас не основной язык.
Дело в том, что то, что Вы написали, применимо к объектам типа std::function ..., а передаваемые аргументы — это указатель на функцию, которые сначала надо завернуть в std::function. Чтобы приведённый оператор сработал, у Вас хотя бы один из аргументов должен уже быть std::function, что ровным счётом то, на что указал автор в примере
std::copy_if(begin(numbers), end(numbers), back_inserter(results), func(isMultiple(2)) || isMultiple(3));
Я бы даже ещё подробнее расписал проблему.
Вроде бы можно написать operator|| для указателей на функции.
Но нет. Потому что это встроенный тип.

Код
#include <vector>
#include <functional>

template<typename ... Args>
using Pred = bool (*) (Args ...);

template<typename ... Args>
std::function<bool(Args ...)> operator||(Pred<Args ...> op1, Pred<Args ...> op2)
{
    return [op1, op2](Args ... args) {return op1(args ...) || op2(args ...);};
}

bool isMultipleOf2(int n)
{
    return (n % 2) == 0;
}

bool isMultipleOf3(int n)
{
    return (n % 3) == 0;
}

int main() {
    auto orr = operator||(&isMultipleOf2, &isMultipleOf3);

    return 0;
}


Более-менее внятную диагностику дают Clang и MSVC.

www.godbolt.org/z/UraL8c
Хех, забавно наблюдать, как монады расползаются из функциональщины и начинают просачиваться везде!
image
Собственно, wiki утверждает, что монада — «это абстракция линейной цепочки связанных вычислений. Монады позволяют организовывать последовательные вычисления.». Что мы здесь и наблюдаем:
or_(isMultipleOf2, isMultipleOf3))

Шашкель и Шкалка для программистов на Плюсах заходят плохо (синтаксис категорически разный), может эта статья более понятно объяснит использование монад в языках, не являющихся функциональными…
Шашкель и Шкалка для программистов на Плюсах заходят плохо (синтаксис категорически разный)

Особенно плохо заходят функции вида:


:.

На плюсах такое проворачивается через дикую магию шаблонов пополам с бустовыми макросами. Писать на плюсах также как на шарпе или яве не выйдет.

Писать на плюсах также как на шарпе или яве не выйдет.

Так как на Шарпах или Жабе — не выйдет. Можно изобрести свой путь. Вообще, монада (в самом первом приближении) — функция принимающая 2 ф-ции и вызывающая их последовательно. А уж как ее оформить, дело десятое. Хоть так:
or_(isMultipleOf2, isMultipleOf3)

Хоть, завернув
isMultipleOf2
в класс обертку и реализовав у нее метод
or_(...)
и тогда писать так:
isMultipleOf2Wrapper.or_(isMultipleOf3)

Как это было, так и останется монадой. Во втором случае, аллокаций просто больше будет.
Вообще, суть моего посыла была в том, что прикольно наблюдать, как паттерны из ФП изобретаются (проникают) и в других языках, не более того.

Мой посыл в том, что любые попытки писать в ФП-стиле на плюсах всегда очень многословно и не факт далеко не факт, что от таких монад код станет чище. Цепочку вызовов, которые просто пробрасывали результат в следующую функцию нельзя скрафтить без какого-нибудь кошмара- это либо класс, который возвращает ссылку на себя, что не чисто с точки зрения функционального языка, либо funcA(funcB(funcC())). Лямбды не умеют самостоятельно захватывать контекст, pattern-matching в обиходе (в том смысле, что я смогу его использовать на работе) появится только через пару лет в лучшем случае. Типажи писать и использовать сложно и больно. std::bind сам по себе неплох, но к нему еще надо функцию написать. А там и в параметры и в return типы подавай, которые нельзя абстрагировать по-человечьи из-за бесчеловечности типажей и пока отсутствующих контрактов. Нет возможности скрафтить новые типы, чтобы не уметь складывать килобайты с метрами, не написав хотя бы пол сотни строк оберток и перегрузок операторов.
И в лучшем случае все эти абстракции просто поднимут время компиляции проекта, в худшем оно еще и накладных расходов накинет. Но, да, наблюдать интересно.
Из недавнего радует что хотя бы структурное связывание появилось в cxx17.

такой оператор будет оператором || для всех типов. Это было бы слишком навязчиво для остальной части кода.

SFINAE + is_invocable (или просто концепт invocable в C++20) в помощь.
Дождаться C++20 и можно ещё проще и без буста:

#include <ranges>
...

int main()
{
    auto const numbers = std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto results = std::vector<int>{};
 
    using std::views;
    results = numbers | filter(isMultipleOf2) | filter(isMultipleOf3);
}
Зарегистрируйтесь на Хабре, чтобы оставить комментарий