注: 引数の数を制限すると、ここに示されているバインダーの機能が損なわれます。
私の解決策は、渡されたプレースホルダーを数えて、使用される最大のプレースホルダーを決定することに基づいています。このエラーを指摘してくれた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
であるためbinder
、部分的な特殊化によってこの概念を表現できます。
namespace std
{
template<class T, int N>
struct is_bind_expression< strict_binder<T, N> >
: public true_type
{};
}
あとは、s を生成する関数テンプレートを作成するだけ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(..) () )
2 番目のオーバーロードのは、 /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);
}