基本的には、拡張子はstd::function
です。
std::function<Sig>
は、その特定の署名をモデル化することができる型消去された呼び出し可能型です。私たちは、その機能の全てを望んでいますが、より多くのシグネチャを持っており、それらのシグネチャのすべてがオーバーロード可能になっています。これが面倒になるところでは、過負荷の線形スタックが必要です。この回答は、新しいC++ルールでは、using宣言でパラメータパックを展開できることを前提としています。また、この答えは、必要に応じてすべてのコピー/ムービーを避けることに焦点を当てていません、私は足場を構築しています。また、より多くのSFINAEが必要です。
まず、私たちは与えられた署名のための仮想呼び出し演算子が必要です。一緒に、これらのグループに
template <class Sig>
struct virt_oper_base;
template <class R, class... Args>
struct virt_oper_base<R(Args...)>
{
virtual R call(Args...) = 0;
};
そして何かを:
今
template <class... Sigs>
struct base_placeholder : virt_oper_base<Sigs>...
{
virtual ~base_placeholder() = default;
using virt_oper_base<Sigs>::call...; // <3
virtual base_placeholder* clone() = 0; // for the copy constructor
};
迷惑な部分。それらのそれぞれを無効にするには、placeholder<F, Sigs...>
が必要です。call()
そこにこれを行うには良い方法かもしれませんが、私はあると考えることができる最高の方法は、2つのtypelistにテンプレートパラメータを持っていると我々は彼らと仕上げとしてだけで一方から他方への各署名を移動するには:
template <class... >
struct typelist;
template <class F, class Done, class Sigs>
struct placeholder_impl;
template <class F, class... Done, class R, class... Args, class... Sigs>
struct placeholder_impl<F, typelist<Done...>, typelist<R(Args...), Sigs...>>
: placeholder_impl<F, typelist<Done..., R(Args...)>, typelist<Sigs...>>
{
using placeholder_impl<F, typelist<Done..., R(Args...)>, typelist<Sigs...>>::placeholder_impl;
R call(Args... args) override {
return this->f(args...);
}
};
template <class F, class... Done>
struct placeholder_impl<F, typelist<Done...>, typelist<>>
: base_placeholder<Done...>
{
placeholder_impl(F f) : f(std::move(f)) { }
F f;
};
template <class F, class... Sigs>
struct placeholder :
placeholder_impl<F, typelist<>, typelist<Sigs...>>
{
using placeholder_impl<F, typelist<>, typelist<Sigs...>>::placeholder_impl;
base_placeholder<Sigs...>* clone() override {
return new placeholder<F, Sigs...>(*this);
}
};
これを私が階層を描くともっと意味があるかもしれません。 void(A&)
とvoid(B&)
:
virt_oper_base<void(A&)> virt_oper_base<void(B&)>
virtual void(A&) = 0; virtual void(B&) = 0;
↑ ↑
↑ ↑
base_placeholder<void(A&), void(B&)>
virtual ~base_placeholder() = default;
virtual base_placeholder* clone() = 0;
↑
placeholder_impl<F, typelist<void(A&), void(B&)>, typelist<>>
F f;
↑
placeholder_impl<F, typelist<void(A&)>, typelist<void(B&)>>
void call(B&) override;
↑
placeholder_impl<F, typelist<>, typelist<void(A&), void(B&)>>
void call(A&) override;
↑
placeholder<F, void(A&), void(B&)>
base_placeholder<void(A&), void(B&)>* clone();
私たちは、与えられた関数は、署名を満たすかどうかを確認する方法が必要です:
template <class F, class Sig>
struct is_sig_callable;
template <class F, class R, class... Args>
struct is_sig_callable<F, R(Args...)>
: std::is_convertible<std::result_of_t<F(Args...)>, R>
{ };
そして今、私たちはまさにそれのすべてを使用するのは、私たちはあなたの2人の署名を持っているとしましょう。私たちはトップレベルのfunction
クラスを持っています。このクラスはbase_placeholder
のメンバーを持ち、そのライフタイムは管理しています。
template <class... Sigs>
class function
{
base_placeholder<Sigs...>* holder_;
public:
template <class F,
std::enable_if_t<(is_sig_callable<F&, Sigs>::value && ...), int> = 0>
function(F&& f)
: holder_(new placeholder<std::decay_t<F>, Sigs...>(std::forward<F>(f)))
{ }
~function()
{
delete holder_;
}
function(function const& rhs)
: holder_(rhs.holder_->clone())
{ }
function(function&& rhs) noexcept
: holder_(rhs.holder_)
{
rhs.holder_ = nullptr;
}
function& operator=(function rhs) noexcept
{
std::swap(holder_, rhs.holder_);
return *this;
}
template <class... Us>
auto operator()(Us&&... us)
-> decltype(holder_->call(std::forward<Us>(us)...))
{
return holder_->call(std::forward<Us>(us)...);
}
};
そして今、我々は、値のセマンティクスを持つ関数オブジェクトを消去したマルチ署名、種類を持っています。あなたが望んでいるのはちょうどです:
std::vector<function<void(A&), void(B&)>> actions;
autoは、C++ 14(https://ideone.com/4931Ht)のTestRunnerの有効な関数パラメータです。 – infiniteLoop
その多態性の解は非常に賢い+1です。 –
@infiniteLoop [auto]で[wandbox](http://melpon.org/wandbox/permlink/qrrFmVRri4bxM0cy)で実行してみてください。これはGCCの拡張であり、C++ 14の空き関数の有効なパラメータではないことに注意してください。 – skypjack