我将在 C++17 中执行此操作。
首先我们从覆盖的想法开始:
template<class T>
struct override_helper { using type=T; };
template<class T>
using override_helper_t = typename override_helper<T>::type;
template<class R, class...Args>
struct override_helper<R(*)(Args...)> {
struct type {
R(*f)(Args...);
R operator()(Args...args)const { return f(std::forward<Args>(args)...); }
type(R(*in)(Args...)):f(in) {}
};
};
template<class R, class...Args>
struct override_helper<R(&)(Args...)>:override_helper<R(*)(Args...)> {
using override_helper<R(*)(Args...)>::override_helper;
};
template<class...Fs>
struct override:override_helper_t<Fs>... {
using override_helper_t<Fs>::operator()...;
override(Fs...fs):override_helper_t<Fs>(std::move(fs))... {}
};
现在我们可以这样做了:
auto f = override(
[](X& x) {do operations on x},
[](Z& z) {do operations on z},
[](auto&) {default operation goes here}
);
f 现在是一个变体风格的访问者,接受 X、Y 和 Z。
然后我们重写 Object.要么暴露variant,要么我们伪造它。
template<class D, class Sig>
struct function_invoker;
template<class D, class R, class...Args>
struct function_invoker<D, R(Args...)> {
R operator()(Args...args)const {
return f(
static_cast<D const*>(this)->get(),
std::forward<Args>(args)...
);
}
template<class F>
function_invoker( F&& fin ):
f([](void* ptr, Args&&...args)->R{
auto* pf = static_cast<std::remove_reference_t<F>*>(ptr);
return (*pf)(std::forward<Args>(args)...);
})
{}
private:
R(*f)(void*, Args&&...) = 0;
};
template<class...Sigs>
struct function_view :
private function_invoker<function_view<Sigs...>, Sigs>...
{
template<class D, class Sig>
friend class function_invoker;
using function_invoker<function_view<Sigs...>, Sigs>::operator()...;
template<class F,
std::enable_if_t< !std::is_same<std::decay_t<F>, function_view>{}, bool> =true
>
function_view( F&& fin ):
function_invoker<function_view<Sigs...>, Sigs>( fin )...,
ptr((void*)std::addressof(fin))
{}
explicit operator bool() const { return ptr; }
private:
void* get() const { return ptr; }
void* ptr = 0;
};
这是一个多重签名可调用函数指针类型擦除。
using Visitor = function_view< void(X&), void(Y&), void(Z&) >;
Visitor 现在是任何可调用的视图类型,可以使用任何 X、Y 或 Z 引用来调用。
struct Object
{
virtual void accept(Visitor visitor) = 0;
};
template<class D>
struct Object_derived:Object {
virtual void accept(Visitor visitor) final override {
visitor(*static_cast<D*>(this));
}
};
struct X:Object_derived<X> {};
struct Y:Object_derived<Y> {};
struct Z:Object_derived<Z> {};
现在您可以将[](auto&){} 传递给Object::accept 并编译。
然后我们连接覆盖,并传入一个带有合适覆盖的可调用对象。
function_view 存储一个指向覆盖对象的指针和一个说明如何调用每个覆盖的函数指针。
实现accept时会选择哪一个。
Live example.
我在这里所做的所有事情都可以在 c++11 中完成,但在 c++17 中要容易得多,所以我在那里做了概念证明。
function_view 可能需要 SFINAE 友好的 ctor 来检测其参数是否满足所有签名,但我很懒。