显然,在 g++ 中,它允许您将一个指向具有 0 个参数的成员函数的指针转换为函数指针,并且调用它会导致调用带有损坏/丢失的 this 指针的该成员函数。
此外,表达式b.*m,其中m 是一个成员函数,b 是一个类将评估虚拟调度,然后可以将生成的对象强制转换为已评估该虚拟调度的函数指针.它仍然会有一个损坏的this 值。
这两种都是标准下的非法操作;程序要么定义不正确,要么使用它是未定义的行为。
在 g++ 中,this 指针是垃圾这一事实是这不是一个非常有用的策略的一个很好的理由。 Here 是一个在 B 中以某种状态构建的示例,它不能在 clang++ 上编译并且在 g++ 上出现段错误。
成员函数和指向对象的指针是两件事。将它们存储在一个东西(函数指针)中是行不通的,无论你如何破解它。
有很多方法可以解决这个问题。最简单的就是使用std::function,但是你被禁止使用标准库。
下一个最简单的方法是编写自己的std::function 变体。我已经做到了——一个支持存储指向 lambdas 的指针、指向函数的指针(指向对象的指针 + 指向成员函数的指针)的“委托”,这些指针与给定的签名兼容。
所有这些都是微不足道的“普通旧数据”,因此您可以创建一个简单的缓冲区,将它们的值复制到缓冲区中,并保存一个知道如何获取指向缓冲区的指针并调用它的函数。
template<class Sig, unsigned Sz=3*sizeof(void*)>
struct fun;
template<class R, class...Args, unsigned Sz>
struct fun<R(Args...), Sz> {
using invoker_t = R(*)(void*, Args&&...);
invoker_t invoker;
unsigned char buffer[Sz];
template<class D>
struct invoker_base_t {
using invoker_sig = R(*)(void* self, Args&&...args);
static invoker_sig invoker() {
return [](void* self, Args&&...args)->R {
return (*static_cast<D*>(self))(static_cast<Args&&>(args)...);
};
}
};
template<class T, class M>
struct mem_fun_invoker_t:invoker_base_t<mem_fun_invoker_t<T,M>> {
T* t;
M m;
mem_fun_invoker_t(T* tin, M min):t(tin), m(min) {}
mem_fun_invoker_t(mem_fun_invoker_t const&)=default;
R operator()(Args&&...args) {
return (t->*m)(static_cast<Args&&>(args)...);
}
};
template<class T, class M>
static mem_fun_invoker_t<T, M> mem_fun_invoker(T* t, M m){ return {t, m}; }
template<class T>
struct ptr_invoker_t:invoker_base_t<ptr_invoker_t<T>> {
T* t;
ptr_invoker_t(T* tin):t(tin) {}
ptr_invoker_t(ptr_invoker_t const&)=default;
R operator()(Args&&...args) {
return (*t)(static_cast<Args&&>(args)...);
}
};
template<class T>
static ptr_invoker_t<T> ptr_invoker(T* t){ return {t}; }
template<class T, class U, class Ret, class...Ts>
fun(T* t, Ret(U::*mem)(Ts...)) {
auto invoke = mem_fun_invoker( t, mem );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
template<class T, class U, class Ret, class...Ts>
fun(T const* t, Ret(U::*mem)(Ts...)const) {
auto invoke = mem_fun_invoker( t, mem );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
template<class Ret, class...Ts>
fun(Ret(*f)(Ts...)) {
auto invoke = ptr_invoker( f );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
// TODO: SFINAE
template<class Lambda>
fun(Lambda&& lambda) {
auto invoke = ptr_invoker( &lambda );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
R operator()(Args...args) {
return invoker(&buffer, static_cast<Args&&>(args)...);
}
explicit operator bool()const {return invoker;}
};
以上需要质量扫描和更多断言和一些 SFINAE。
But it works.
fun<Sig> 是std::function 的基本变体,几乎没有状态。缺少的 SFINAE 需要测试 Lambda 是否与签名兼容并且不等于 fun 类型本身。
这些对象的大小大约为 4 个指针;您可以使用 Sz 参数配置多大。 2 可能足以容纳成员函数和 this 指针。我四舍五入到 3。
这些东西也可以存储指向函数的指针。
它们可以存储指向 lambda 的指针或fun 的变体,但不会延长所述 lambda 的生命周期。这很危险,但通常很有用。
与std::function 一样,它们不需要签名完全匹配。与 C++14 的 std::function 不同,如果 R 是 void,则存储的对象也必须返回 void。解决这个问题需要更多工作:
namespace details {
template<class Sig, class D>
struct invoker_base_t;
template<class R, class...Args, class D>
struct invoker_base_t<R(Args...), D> {
using invoker_sig = R(*)(void* self, Args&&...args);
static invoker_sig invoker() {
return [](void* self, Args&&...args)->R {
return (*static_cast<D*>(self))(static_cast<Args&&>(args)...);
};
}
};
template<class...Args, class D>
struct invoker_base_t<void(Args...), D> {
using invoker_sig = void(*)(void* self, Args&&...args);
static invoker_sig invoker() {
return [](void* self, Args&&...args)->void {
(*static_cast<D*>(self))(static_cast<Args&&>(args)...);
};
}
};
template<class Sig, class T, class M>
struct mem_fun_invoker_t;
template<class R, class...Args, class T, class M>
struct mem_fun_invoker_t<R(Args...), T, M>:
invoker_base_t<R(Args...), mem_fun_invoker_t<R(Args...),T,M> >
{
T* t;
M m;
mem_fun_invoker_t(T* tin, M min):t(tin), m(min) {}
mem_fun_invoker_t(mem_fun_invoker_t const&)=default;
R operator()(Args&&...args) {
return (t->*m)(static_cast<Args&&>(args)...);
}
};
template<class...Args, class T, class M>
struct mem_fun_invoker_t<void(Args...), T, M>:
invoker_base_t<void(Args...), mem_fun_invoker_t<void(Args...),T,M> >
{
T* t;
M m;
mem_fun_invoker_t(T* tin, M min):t(tin), m(min) {}
mem_fun_invoker_t(mem_fun_invoker_t const&)=default;
void operator()(Args&&...args) {
(t->*m)(static_cast<Args&&>(args)...);
}
};
template<class Sig, class T>
struct ptr_invoker_t;
template<class R, class...Args, class T>
struct ptr_invoker_t<R(Args...), T>:
invoker_base_t<R(Args...), ptr_invoker_t<R(Args...),T> >
{
T* t;
ptr_invoker_t(T* tin):t(tin) {}
ptr_invoker_t(ptr_invoker_t const&)=default;
R operator()(Args&&...args) {
return (*t)(static_cast<Args&&>(args)...);
}
};
template<class...Args, class T>
struct ptr_invoker_t<void(Args...), T>:
invoker_base_t<void(Args...), ptr_invoker_t<void(Args...),T> >
{
T* t;
ptr_invoker_t(T* tin):t(tin) {}
ptr_invoker_t(ptr_invoker_t const&)=default;
void operator()(Args&&...args) {
(*t)(static_cast<Args&&>(args)...);
}
};
}
template<class Sig, unsigned Sz=3*sizeof(void*)>
struct fun;
template<class R, class...Args, unsigned Sz>
struct fun<R(Args...), Sz> {
using invoker_t = R(*)(void*, Args&&...);
invoker_t invoker;
unsigned char buffer[Sz];
template<class T, class M>
static details::mem_fun_invoker_t<R(Args...), T, M> mem_fun_invoker(T* t, M m){ return {t, m}; }
template<class T>
static details::ptr_invoker_t<R(Args...), T> ptr_invoker(T* t){ return {t}; }
template<class T, class U, class Ret, class...Ts>
fun(T* t, Ret(U::*mem)(Ts...)) {
auto invoke = mem_fun_invoker( t, mem );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
template<class T, class U, class Ret, class...Ts>
fun(T const* t, Ret(U::*mem)(Ts...)const) {
auto invoke = mem_fun_invoker( t, mem );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
template<class Ret, class...Ts>
fun(Ret(*f)(Ts...)) {
auto invoke = ptr_invoker( f );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
// TODO: SFINAE
template<class Lambda>
fun(Lambda&& lambda) {
auto invoke = ptr_invoker( &lambda );
static_assert(sizeof(invoke) <= Sz);
::new( (void*)buffer ) decltype(invoke)(invoke);
invoker = invoke.invoker();
}
R operator()(Args...args) {
return invoker(&buffer, static_cast<Args&&>(args)...);
}
explicit operator bool()const {return invoker;}
};
live example.
以上内容符合标准,不依赖于std。编写 SFINAE 以确保在糟糕的情况下不会调用 Lambda&& 重载可能需要重写一些 std,例如 std::is_same 和 enable_if。