このようなことは、(メンバー) 関数ポインターでは簡単に可能ですが、潜在的にオーバーロードされた を持つファンクターのoperator()
場合、これは非常に困難になります。関数が受け取る引数の数を知る方法があると仮定した場合 (そしてコンテナーが実際にその数の要素を持っていると仮定した場合)、単にインデックス トリックstd::next
を使用してベクトルを引数リストに展開できます。begin()
イテレータ:
#include <utility>
#include <iterator>
template<class F, class Args, unsigned... Is>
auto invoke(F&& f, Args& cont, seq<Is...>)
-> decltype(std::forward<F>(f)(*std::next(cont.begin(), Is)...))
{
return std::forward<F>(f)(*std::next(cont.begin(), Is)...);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
この実装は、ランダム アクセス コンテナに対しては非常にうまく機能しますが、フォワードおよび特に入力コンテナに対してはあまりうまく機能しません。これらをパフォーマンスの高い方法で機能させるために、展開されたステップごとにイテレータをインクリメントするルートに進むことを試みるかもしれませんが、問題に遭遇します: 関数への引数の評価順序が指定されていないため、おそらく引数を間違った順序で渡します。
幸いなことに、左から右への評価を強制する方法があります: リスト初期化構文です。ここで、引数を渡すために使用できるコンテキストが必要です。可能なコンテキストは、オブジェクトを構築し、コンストラクターを介して関数と引数を渡し、そこで関数を呼び出すことです。ただし、コンストラクターは値を返すことができないため、返された値を取得する機能は失われます。
私が考えたのは、正しい要素を指すiteratorの配列を作成し、それらが逆参照される 2 番目のステップでそれらを再度展開することです。
#include <utility>
template<class T> using Alias = T; // for temporary arrays
template<class F, class It, unsigned N, unsigned... Is>
auto invoke_2(F&& f, It (&&args)[N], seq<Is...>)
-> decltype(std::forward<F>(f)(*args[Is]...))
{
return std::forward<F>(f)(*args[Is]...);
}
template<class F, class Args, unsigned... Is>
auto invoke_1(F&& f, Args& cont, seq<Is...> s)
-> decltype(invoke_2(std::forward<F>(f), std::declval<decltype(cont.begin())[sizeof...(Is)]>(), s))
{
auto it = cont.begin();
return invoke_2(std::forward<F>(f), Alias<decltype(it)[]>{(void(Is), ++it)...}, s);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
コードは GCC 4.7.2 に対してテストされ、宣伝どおりに動作します。
渡されるファンクターは s であると言ったので、std::function
それらが取る引数の数を取得するのは非常に簡単です。
template<class F> struct function_arity;
// if you have the 'Signature' of a 'std::function' handy
template<class R, class... Args>
struct function_arity<R(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>{};
// if you only have the 'std::function' available
template<class R, class... Args>
struct function_arity<std::function<R(Args...)>>
: function_arity<R(Args...)>{};
上記の作業function_arity
から作成する必要さえないことに注意してください。invoke
std::function
template<class R, class... Ts, class Args>
R invoke(std::function<R(Ts...)> const& f, Args& cont){
return invoke_1(f, cont, gen_seq<sizeof...(Ts)>{})
}