提问人:hlebyshek 提问时间:11/7/2023 最后编辑:hlebyshek 更新时间:11/8/2023 访问量:114
我可以使用 std::bind* 编写函数吗?
Can I compose functions using std::bind*?
问:
我正在玩 C++20 中的函数式编程并编写类似的东西:
template <class OuterFn, class InnerFn, class... Args>
concept Composable =
std::invocable<OuterFn, std::invoke_result_t<InnerFn, Args...>>;
template <class OuterFn, class InnerFn>
constexpr auto
compose(OuterFn&& outer, InnerFn&& inner)
{
return [
out = std::forward<OuterFn>(outer),
in = std::forward<InnerFn>(inner)
]<class... Args>(Args && ... args)
requires Composable<OuterFn, InnerFn, Args...>
{
using std::invoke, std::forward;
return invoke(out, invoke(in, forward<Args>(args)...));
};
}
template <class OuterFn, class InnerFn>
constexpr auto
operator*(OuterFn&& outer, InnerFn&& inner)
{
using std::forward;
return compose(forward<OuterFn>(outer), forward<InnerFn>(inner));
}
template <class... RestFn>
constexpr auto
compose(RestFn&&... rest)
{
return (std::forward<RestFn>(rest) * ...);
}
这是工作,但我想通过使用 std::bind* 而不是 lambda 来重构两个参数的 compose。 所以,我认为这个:
using eq_t = std::equal_to<const std::string_view>;
constexpr auto eq_42 = std::bind_front(eq_t{}, "42");
显然是(特别是如果使用没有重载运算符的函数):
constexpr auto eq_42 = [](const std::string_view sv){ return sv == "42"; };
也许你有一个想法,我该怎么做,或者我不能这样做的原因?
问题是如何检索内部函数的参数。
std::bind(outer, std::bind_front(inner));
我通过类比一个参数来尝试可变模板参数(以及数百万个其他变体),但它不起作用。std::bind(outer, std::bind(inner, _1));
P.S. 对不起我的英语)
答:
0赞
Caleth
11/8/2023
#1
不能将模板替换为使用 的实现,因为需要为调用提供可变数量的占位符。你能做的最好的事情就是支持内部调用的特定参数,直到(实现定义的)数量限制。compose
std::bind
bind
std::placeholders
也不能使用 ,因为函数对象通常不是它在调用时返回的值。std::bind_front
因此,您必须有一个中间函数对象,其中包含内部和外部函数,最简单的语法是 lambda。如果你真的想,你可以把它包装起来,但没有意义。std::bind_front
template <class OuterFn, class InnerFn>
constexpr auto
compose(OuterFn&& outer, InnerFn&& inner)
{
using std::invoke, std::forward;
struct composer {
template <class Out, class In, class... Args>
auto operator()(Out&& out, In&& in, Args&&.. args) {
return invoke(out, invoke(in, forward<Args>(args)...));
}
};
// Fairly gratuitous bind_front, composer could have done the capturing of outer and inner itself
return std::bind_front(composer{}, forward(outer), forward(inner));
}
评论
0赞
hlebyshek
11/8/2023
这就是为什么我写了 , 意思是 , ,std::bind*
std::bind
std::bind_front
std::bind_back
0赞
Caleth
11/8/2023
@hlebyshek答案仍然是否定的,因为您需要等待结果才能进行论证inner
outer
1赞
n. m. could be an AI
11/8/2023
#2
您需要几个实用程序类。
#include <utility>
#include <tuple>
#include <functional>
template <typename Func>
struct wrap
{
wrap (Func func) : func{func} {}
template <typename ... T>
auto operator()(std::tuple<T...>&& args) {
return std::apply(func, std::forward<std::tuple<T...>>(args));
}
Func func;
};
template <typename Func>
struct unwrap
{
unwrap (Func func) : func{func} {}
template <typename ... T>
auto operator()(T&& ... args) {
return func(std::tuple(std::forward<T>(args)...));
}
Func func;
};
有了这个:
template <typename Func1, typename Func2>
auto compose (Func1 func1, Func2 func2)
{
using namespace std::placeholders;
return unwrap(std::bind(func1, std::bind(wrap(func2), _1)));
}
还有一个测试:
int foo(int a, int b) { return a+b; }
int bar(int a) { return a*2; }
int main()
{
std::cout << compose(bar, foo)(3,4);
}
概念化和前瞻性留给读者作为练习。
但是你真的应该忘记并使用lambdas。std::bind
评论
0赞
hlebyshek
11/9/2023
谢谢你的回答。在我的情况下,额外的代码将是多余的。我会忍受使用 lambda)
评论
std::bind
std::bind
与 lambda 相比,它不是“简单、可读、简洁”std::bind()
<functional>
bind()
std::bind()