注意:限制参数的数量会破坏活页夹的一项功能,此处说明。
我的解决方案是基于计算通过的占位符来确定使用的最大占位符。感谢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)>
{};
这为活页夹的用户增加了正确计算数量的负担。对于某些绑定的 Callable,例如函数指针,可以推断出参数的数量(这也允许自动提供所需数量的占位符)。一旦你以任何一种方式固定了参数的数量,就很容易编写一个包装器来存储一个活页夹并提供一个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
is a binder
,您可以通过部分特化来表达这个概念:
namespace std
{
template<class T, int N>
struct is_bind_expression< strict_binder<T, N> >
: public true_type
{};
}
剩下的就是编写一个生成strict_binder
s 的函数模板。这是一个类似于的版本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(..) () )
,第二个重载 ofstrict_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);
}