これはやや一般的なやり方で行うことができます。
template<class...>struct types{};
template<std::size_t I>using index=std::integral_constant<std::size_t, I>;
template<class T, class types>
struct get_index_of_type;
template<class T, class...Ts>
struct get_index_of_type<T, types<T,Ts...>>:
index<0>
{};
template<class T, class U, class...Ts>
struct get_index_of_type<T, types<U,Ts...>>:
index<get_index_of_type<T, types<Ts...>>{}+1>
{};
template<class R, class Types>
struct dynamic_dispatch;
template<class R, class...Ts>
struct dynamic_dispatch<R, types<Ts...>>
{
using fptr = R(*)(void const* pf, void* t);
template<class F>
std::array<fptr, sizeof...(Ts)>
make_table() const {
return {{
+[](void const* pf, void* t)->R{
auto* pt = static_cast< std::remove_reference_t<Ts>* >(t);
auto* f = static_cast< std::remove_reference_t<F> const* >(pf);
return (*f)(static_cast<Ts&&>(*pt));
}...
}};
}
void const* pf = nullptr;
std::array<fptr, sizeof...(Ts)> table;
dynamic_dispatch(dynamic_dispatch&&)=default;
dynamic_dispatch(dynamic_dispatch const&)=default;
dynamic_dispatch& operator=(dynamic_dispatch&&)=default;
dynamic_dispatch& operator=(dynamic_dispatch const&)=default;
template<class F,
std::enable_if_t< !std::is_same<std::decay_t<F>, dynamic_dispatch>{}, int> =0
>
dynamic_dispatch(F&& f):
pf(std::addressof(f)),
table(make_table<std::decay_t<F>>())
{}
template<class T>
R operator()(T&& t) const {
return table[get_index_of_type<T,types<Ts...>>{}](pf, std::addressof(t));
}
};
dynamic_dispatch<R, types<a,b,c>>
が必要なL/Rの値とconstのを含め
a
、
b
または
c
(正確にタイプのいずれかを呼び出すことができる任意の呼び出し可能になりますので、詳細なリストを作るん暗黙のキャストが行われていません;これはできますより多くの作業で修正することができます)。今
、apply
と呼ばbase
にメソッドを追加:
virtual void apply(dynamic_dispatch<void, types<base*, derived*>> f) {
return f(this);
}
オーバーライドそれを派生で:
virtual void apply(dynamic_dispatch<void, types<base*, derived*>> f) override {
return f(this);
}
を同じ身体で。メインで今
:
auto super_foo = [](auto* x) {
return foo(*x);
};
int main()
{
base* ptr1 = new(base);
base* ptr2 = new(derived);
ptr1->apply(super_foo);
ptr2->apply(super_foo);
}
live example。
さらに読むために、関数オブジェクト上の型のリストに対して消去された動的ディスパッチをタイプします。私はこのディスパッチのためのビューを作成しました。
super_foo
は、全体のオーバーロードセットfoo
を表す単一のオブジェクトであり、1つのパラメータとして渡すことができます。
これはビジターパターンでより多くの従来もを行うことができます。そして、あなたがfoo_visitor
を実装
struct visitor {
void invoke(base*) const = 0;
void invoke(derived*) const = 0;
};
:
struct foo_visitor:visitor {
void invoke(base* a) const {return foo(*a);}
void invoke(derived* a) const {return foo(*a);}
};
、我々はvisitor&
を取り、それに.invoke(this)
を行うapply
を書きます。
(注)このテクニックこと、あなたはそれがスマートに正確な一致を必要とTs
代わりの間で選択させる場合は特に、再帰を介して複数の派遣多型を可能にする(または、複数の種類のパックでdynamic_dispatch< R, types... >
にdynamic_dispatch
を変えることができます)。
@tkausl - ベースのバージョンをオーバーライドする必要があります。 OPの問題は、オーバーライドするのではなく、隠れている、オーバーロードしているということではありませんが、コンパイラはベースのバージョンの関数とその戻り値の型を見ています。 –
@tkauslはい、それはそれを無効にします。あなたのデモは、[ここ](https://ideone.com/jSqb17)です。実際の理由は、関数呼び出しは実行時ではなくコンパイル時に解決されるということです。 'ptr2'は' base * '型で' base :: downcast'は 'base&'を返すので、 'void foo(const base&a)'が呼び出されます。 –
動作しません。 'foo'を仮想メンバ関数にするか、Visitorパターンを使いたいかもしれません。 –