根据http://en.cppreference.com/w/cpp/utility/functional/bind,对于std::bind
成员函数operator()
...
如果对g()的调用中提供的某些参数不是
与存储在g中的任何占位符匹配,未使用的参数为
评估并丢弃。
引用示例,可以执行以下操作:
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
的变体来禁止这种行为?这里有什么可能性和困难。谢谢
最佳答案
注意:限制参数数量会破坏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)>
{};
这给正确计算活页夹使用者的数量带来了负担。对于某些绑定的Callables(例如函数指针),可以推断出参数的数量(这还可以自动提供所需数量的占位符)。一旦您以任何一种方式固定了参数数量,就很容易编写一个存储绑定程序并提供一个
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
的函数,当未传递任何占位符时调用绑定函数: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)... );
}
这使用标记分派来返回绑定程序或调用函数的结果。
我放弃了正确格式化的格式,您可能想在
detail
名称空间中引入别名模板。注意,在
decltype( std::bind(..) () )
的第二个重载中的strict_bind_or_call
是重现INVOKE
/ 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++ - c + +:如何编写一个std::bind-like对象检查过多的参数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21762844/