选项3怎么样?
template<auto* operation, class Sig = void()>
struct can_do;
template<auto* operation, class R, class...Args>
struct can_do<operation, R(Args...)> {
void* pstate = 0;
R(*poperation)(void*, Args&&...) = 0;
template<class T,
std::enable_if_t<std::is_convertible_v<
std::invoke_result_t<decltype(*operation), T&&, Args&&...>,
R>,
bool> = true,
std::enable_if_t<!std::is_same_v<can_do, std::decay_t<T>>, bool> =true
>
can_do(T&& t):
pstate((void*)std::addressof(t)),
poperation(+[](void* pstate, Args&&...args)->R {
return (*operation)( std::forward<T>(*static_cast<std::remove_reference_t<T>*>(pstate)), std::forward<Args>(args)... );
})
{}
can_do(can_do const&)=default;
can_do(can_do&&)=default;
can_do& operator=(can_do const&)=default;
can_do& operator=(can_do&&)=default;
~can_do()=default;
auto operator->*( decltype(operation) ) const {
return [this](auto&&...args)->R {
return poperation( pstate, decltype(args)(args)... );
};
}
};
现在你可以做
auto invoke_f = [](auto&& elem)->void { elem.f(); };
struct UseA
{
UseA(can_do<&invoke_f> a) : m_a(a){}
void f(){(m_a->*&invoke_f)();}
can_do<&invoke_f> m_a;
};
测试代码:
struct A {
void f() { std::cout << "hello world"; }
};
struct A2 {
void f() { std::cout << "goodbye"; }
};
A a;
UseA b(a);
b.f();
A2 a2;
UseA b2(a2);
b2.f();
活生生的例子。
拥有更丰富的多操作界面留作can_do
练习。
UseA
不是模板。 A
并且A2
没有通用的基接口类。
然而它确实有效。