gpt4 book ai didi

c++:如何编写一个类似 std::bind 的对象来检查多余的参数?

转载 作者:行者123 更新时间:2023-11-28 07:05:57 24 4
gpt4 key购买 nike

根据 http://en.cppreference.com/w/cpp/utility/functional/bind , 对于 std::bind

Member function operator()

...

If some of the arguments that are supplied in the call to g() are not matched by any placeholders stored in g, the unused arguments are evaluated and discarded.

引用例子,可以做到:

void f(int n1, int n2, int n3, const int& n4, int n5) {
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}

int main() {
auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
}

如果你把 f1 当作一个有 5 个参数的函数,其中 3 个是固定的,那么按常理你应该不能调用有 3 个参数的 f1。但是,如上面的代码所示,您可以这样做,因为额外的参数会被静默忽略。

根据 Why do objects returned from bind ignore extra arguments? ,这种行为之所以存在,是因为实现起来方便??。

对我来说,这是一个相当令人困惑的库功能,因为它打乱了函数的数量(例如上例中的 5 - 3 = 3)并且打败了关于函数调用的推理。我想知道是否有任何实际用例可以证明这种行为实际上是有益的?

更重要的是,是否可以实现 std::bind 的变体来禁止这种行为?这里的可能性和困难是什么。

谢谢

最佳答案

注意:限制参数的数量会破坏 Binder 的一个特性,如图所示 here .

我的解决方案基于 计算传递的占位符 确定使用的最大占位符。感谢Xeo指出这个错误。

#include <functional>
#include <type_traits>
#include <utility>

template<class T, class U>
constexpr auto c_max(T&& t, U&& u)
-> typename std::remove_reference<decltype( t > u ? t : u )>::type
{ return t > u ? t : u; }

template<class...>
struct max_placeholder : std::integral_constant<int, 0> {};

template<class T, class... Rest>
struct max_placeholder<T, Rest...>
: std::integral_constant<int, c_max(std::is_placeholder<T>::value,
max_placeholder<Rest...>::value)>
{};

这给 Binder 用户带来了正确计算数量的负担。对于某些绑定(bind)的 Callable,例如函数指针,可以推断出参数的数量(这也允许自动提供所需数量的占位符)。一旦您以任何一种方式固定了参数的数量,就很容易编写一个包装器来存储 Binder 并提供一个 operator() 模板来检查参数的数量:

template<class T, int N>
struct strict_binder
{
T binder;

template<class... Args>
auto operator()(Args&&... args)
-> decltype( binder(std::forward<Args>(args)...) )
{
static_assert(sizeof...(args) == N, "wrong number of arguments");
return binder(std::forward<Args>(args)...);
}
};

也有可能产生替换失败而不是错误。

由于 strict_binder 是一个 binder,您可以通过部分特化来表达这个概念:

namespace std
{
template<class T, int N>
struct is_bind_expression< strict_binder<T, N> >
: public true_type
{};
}

剩下的就是编写一个生成 strict_binder 的函数模板。这是一个类似于 std::bind 的版本:

template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> strict_binder<
typename std::decay<
decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
>::type,
max_placeholder<typename std::remove_reference<Args>::type...>::value
>
{
return { std::bind(std::forward<F>(f), std::forward<Args>(args)...) };
}

本质上,返回类型是一个

strict_binder<decltype(std::bind(f, args...)), count_placeholders<Args...>::value>

也就是说,strict_binder 存储了 std::bind 的结果类型。


您还可以编写类似apply 的函数,在未传递占位符时调用绑定(bind)函数:

template<int N, class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, N>, F&& f, Args&&... args)
-> strict_binder<
typename std::decay<
decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
>::type,
N
>
{
return { std::bind( std::forward<F>(f), std::forward<Args>(args)... ) };
}

template<class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, 0>, F&& f, Args&&... args)
-> decltype( std::bind( std::forward<F>(f), std::forward<Args>(args)... ) () )
{
return std::bind( std::forward<F>(f), std::forward<Args>(args)... ) ();
}

template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> decltype( strict_bind_or_call( std::integral_constant<int, max_placeholder<typename std::remove_reference<Args>::type...>::value>{},
std::forward<F>(f), std::forward<Args>(args)... ) )
{
using max_placeholder_here =
max_placeholder<typename std::remove_reference<Args>::type...>;

return strict_bind_or_call( max_placeholder_here{},
std::forward<F>(f), std::forward<Args>(args)... );
}

这使用标记分派(dispatch)来返回绑定(bind)器或调用函数的结果。我放弃了正确格式化,您可能想在 detail 命名空间中引入别名模板。

请注意 strict_bind_or_call 的第二个重载中的 decltype( std::bind(..) () ) 是重现 语义的简单方法调用/绑定(bind);我不能只写 f(args...) 因为 f 可能是一个成员函数。


使用示例:

#include <iostream>

void foo(int p0, int p1)
{ std::cout << "[" << p0 << ", " << p1 << "]\n"; }

int main()
{
auto f0 = strict_bind(foo, std::placeholders::_1, 42);
f0(1);

strict_bind(foo, 1, 2);
}

关于c++:如何编写一个类似 std::bind 的对象来检查多余的参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21762844/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com