将成员函数指针传递给父 class 产生编译器错误
Pass member function pointer to parent class yields compiler error
我想让子 classes 注册回调到他们的父 class 以便父 class 的用户可以调用具有已知函数签名的子的方法.
typedef int(*Func)(int);
class A
{
public:
void registerFunc(Func f)
{}
};
class B : public A
{
public:
B()
{
A::registerFunc(&B::myF);
}
int myF(int x) {
// do stuff with member variables
return 3;
}
};
但是我得到这个编译器错误
main.cpp:18:23: error: cannot initialize a parameter of type 'Func' (aka 'int (*)(int)') with an rvalue of type 'int (B::*)(int)'
A::registerFunc(&B::myF);
^~~~~~~
main.cpp:8:28: note: passing argument to parameter 'f' here
void registerFunc(Func f)
这是一个用简明示例说明错误的 Repl。
https://replit.com/@Carpetfizz/RudeSmoothComments#main.cpp
相关线程中接受的答案建议覆盖 A
中声明的虚函数,但我的用例实际上需要动态回调注册。
你可以试试这个。
typedef std::function<int (int)> Func;
class A
{
public:
void registerFunc(Func f)
{}
};
class B : public A
{
public:
B()
{
A::registerFunc(std::bind(&B::myF, *this, std::placeholders::_1));
}
int myF(int x) {
// do stuff with member variables
return 3;
}
};
如果我理解目标(并且相信我,那是一个粗略的 'if'),您想要指定某些 A
派生的某些成员以从某些 A
成员调用作为派遣 'callback' 机械师。如果是这样,那么在评论中回答你的问题,是的,一个函数和绑定可以做到这一点。在 sfinae 的帮助下甚至可以 semi-protected:
例子
#include <iostream>
#include <type_traits>
#include <functional>
#include <memory>
struct A
{
virtual ~A() = default;
std::function<void(int)> callback = [](int){};
template<class Derived>
std::enable_if_t<std::is_base_of<A, Derived>::value>
registerCallback(void (Derived::*pfn)(int))
{
using namespace std::placeholders;
callback = std::bind(pfn, dynamic_cast<Derived*>(this), _1);
}
void fire(int arg)
{
callback(arg);
}
};
struct B : public A
{
void memberfn(int arg)
{
std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
}
};
struct Foo
{
void memberfn(int arg)
{
std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
}
};
int main()
{
std::unique_ptr<A> ptr = std::make_unique<B>();
ptr->registerCallback(&B::memberfn);
// ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
ptr->fire(42);
}
输出
void B::memberfn(int):42
零件
第一部分很简单。我们将一个成员变量 callback
声明为一个 std::function<void(int)>
实例。这是我们最终将绑定可调用对象点的地方。默认值是不执行任何操作的 lambda。
第二部分……有点复杂:
template<class Derived>
std::enable_if_t<std::is_base_of<A, Derived>::value>
registerCallback(void (Derived::*pfn)(int))
这将registerCallback
声明为一个可用的成员函数,它接受一个non-static成员函数指针以一个int
作为参数,但是只有 如果承载该成员函数的 class 或其中的派生函数是 A
(或 A
本身)的派生。某些具有成员 void foo(int)
的非 A
派生 Foo
将 不会 编译。
接下来,回调本身的设置。
using namespace std::placeholders;
callback = std::bind(pfn, dymamic_cast<Derived*>(this), _1);
这只是将 pointer-to-member 绑定到 this
dynamic-cast 到派生类型(这有更好的工作,否则我们有麻烦了,请参阅此谩骂结尾处的最后警告),并设置 call-time 占位符。您看到的 _1
来自 std::placeholders
命名空间,用于延迟向回调提供参数,直到我们实际调用它(需要它的地方,您会看到之后)。有关详细信息,请参阅 std::placehholders
。
最后,fire
成员执行此操作:
void fire(int arg)
{
callback(arg);
}
这将使用提供的参数调用已注册的函数对象。成员函数 和 this
都已连接到对象中。参数arg
用于填充我们前面提到的占位符
这个测试驱动程序很简单:
int main()
{
std::unique_ptr<A> ptr = std::make_unique<B>();
ptr->registerCallback(&B::memberfn);
// ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
ptr->fire(42);
}
这将创建一个新的 B
,将其托管在一个动态 A
指针中(因此您知道没有有趣的事情在进行)。尽管如此,因为 B
派生自 A
,所以 registerCallback
sfinae 过滤通过了检查并且回调成功注册。然后我们调用 fire
方法,传递我们的 int
参数 42
,它将被发送到回调等
警告:能力越大,责任越大
即使那些有防止传递非A
派生成员函数的保护,也绝对none来自转换本身。制作一个基本的 A
,传递一个 B
成员(这将起作用,因为 A
是它的基础)是微不足道的,但实际上没有 B
。
您可以在运行时通过 dynamic_cast
捕获它,我们目前 不是 错误检查。例如:
registerCallback(void (Derived::*pfn)(int))
{
using namespace std::placeholders;
Derived *p = dynamic_cast<Derived*>(this);
if (p)
callback = std::bind(pfn, p, _1);
}
你可以选择比较冒险的路。就个人而言,我会检测 null 情况并抛出异常以确保安全(呃)
我想让子 classes 注册回调到他们的父 class 以便父 class 的用户可以调用具有已知函数签名的子的方法.
typedef int(*Func)(int);
class A
{
public:
void registerFunc(Func f)
{}
};
class B : public A
{
public:
B()
{
A::registerFunc(&B::myF);
}
int myF(int x) {
// do stuff with member variables
return 3;
}
};
但是我得到这个编译器错误
main.cpp:18:23: error: cannot initialize a parameter of type 'Func' (aka 'int (*)(int)') with an rvalue of type 'int (B::*)(int)'
A::registerFunc(&B::myF);
^~~~~~~
main.cpp:8:28: note: passing argument to parameter 'f' here
void registerFunc(Func f)
这是一个用简明示例说明错误的 Repl。
https://replit.com/@Carpetfizz/RudeSmoothComments#main.cpp
相关线程中接受的答案建议覆盖 A
中声明的虚函数,但我的用例实际上需要动态回调注册。
你可以试试这个。
typedef std::function<int (int)> Func;
class A
{
public:
void registerFunc(Func f)
{}
};
class B : public A
{
public:
B()
{
A::registerFunc(std::bind(&B::myF, *this, std::placeholders::_1));
}
int myF(int x) {
// do stuff with member variables
return 3;
}
};
如果我理解目标(并且相信我,那是一个粗略的 'if'),您想要指定某些 A
派生的某些成员以从某些 A
成员调用作为派遣 'callback' 机械师。如果是这样,那么在评论中回答你的问题,是的,一个函数和绑定可以做到这一点。在 sfinae 的帮助下甚至可以 semi-protected:
例子
#include <iostream>
#include <type_traits>
#include <functional>
#include <memory>
struct A
{
virtual ~A() = default;
std::function<void(int)> callback = [](int){};
template<class Derived>
std::enable_if_t<std::is_base_of<A, Derived>::value>
registerCallback(void (Derived::*pfn)(int))
{
using namespace std::placeholders;
callback = std::bind(pfn, dynamic_cast<Derived*>(this), _1);
}
void fire(int arg)
{
callback(arg);
}
};
struct B : public A
{
void memberfn(int arg)
{
std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
}
};
struct Foo
{
void memberfn(int arg)
{
std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
}
};
int main()
{
std::unique_ptr<A> ptr = std::make_unique<B>();
ptr->registerCallback(&B::memberfn);
// ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
ptr->fire(42);
}
输出
void B::memberfn(int):42
零件
第一部分很简单。我们将一个成员变量 callback
声明为一个 std::function<void(int)>
实例。这是我们最终将绑定可调用对象点的地方。默认值是不执行任何操作的 lambda。
第二部分……有点复杂:
template<class Derived>
std::enable_if_t<std::is_base_of<A, Derived>::value>
registerCallback(void (Derived::*pfn)(int))
这将registerCallback
声明为一个可用的成员函数,它接受一个non-static成员函数指针以一个int
作为参数,但是只有 如果承载该成员函数的 class 或其中的派生函数是 A
(或 A
本身)的派生。某些具有成员 void foo(int)
的非 A
派生 Foo
将 不会 编译。
接下来,回调本身的设置。
using namespace std::placeholders;
callback = std::bind(pfn, dymamic_cast<Derived*>(this), _1);
这只是将 pointer-to-member 绑定到 this
dynamic-cast 到派生类型(这有更好的工作,否则我们有麻烦了,请参阅此谩骂结尾处的最后警告),并设置 call-time 占位符。您看到的 _1
来自 std::placeholders
命名空间,用于延迟向回调提供参数,直到我们实际调用它(需要它的地方,您会看到之后)。有关详细信息,请参阅 std::placehholders
。
最后,fire
成员执行此操作:
void fire(int arg)
{
callback(arg);
}
这将使用提供的参数调用已注册的函数对象。成员函数 和 this
都已连接到对象中。参数arg
用于填充我们前面提到的占位符
这个测试驱动程序很简单:
int main()
{
std::unique_ptr<A> ptr = std::make_unique<B>();
ptr->registerCallback(&B::memberfn);
// ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
ptr->fire(42);
}
这将创建一个新的 B
,将其托管在一个动态 A
指针中(因此您知道没有有趣的事情在进行)。尽管如此,因为 B
派生自 A
,所以 registerCallback
sfinae 过滤通过了检查并且回调成功注册。然后我们调用 fire
方法,传递我们的 int
参数 42
,它将被发送到回调等
警告:能力越大,责任越大
即使那些有防止传递非A
派生成员函数的保护,也绝对none来自转换本身。制作一个基本的 A
,传递一个 B
成员(这将起作用,因为 A
是它的基础)是微不足道的,但实际上没有 B
。
您可以在运行时通过 dynamic_cast
捕获它,我们目前 不是 错误检查。例如:
registerCallback(void (Derived::*pfn)(int))
{
using namespace std::placeholders;
Derived *p = dynamic_cast<Derived*>(this);
if (p)
callback = std::bind(pfn, p, _1);
}
你可以选择比较冒险的路。就个人而言,我会检测 null 情况并抛出异常以确保安全(呃)