试图为会员回拨
Trying to make a callback for a member
我有 class stickyNotes
,其中有函数 addNote
,它是一个 public 非静态函数。
在代码中定义了一个新类型:
typedef void(*fptr)
我还有 class Button
,它在其构造函数中接受类型为 fptr
的变量,我有一个函数 makeButton()
returns a Button
对象。
stickyNotes
还有另一个名为 rendermainWindow
的成员,它呈现主 window 并添加一个按钮,我正在尝试创建一个 fptr
类型的新变量设置为 stickyNotes::addNote
的地址,我收到错误:
'&': illegal operation on bound member function expression
stickyNotes::rendermainWindow
:
void stickyNotes::rendermainWindow() {
/*
Renders the main window
*/
this->buttonList.empty(); // buttonList is a list of all buttons
mainWindow->clear(sf::Color(29, 29, 27, 255)); // clearing the window
sf::Vector2u windowDimensions = mainWindow->getSize(); // getting window dimensions
fptr cb = &this->addNote; <-------- ERROR HERE
Button plusB = makeButton((int)(windowDimensions.x * 0.75),
(int)(windowDimensions.y * 0.15),
(int)(windowDimensions.x * 0.85),
(int)(windowDimensions.y * 0.25),
mainWindow,
cb);
// first button to add stuff
std::vector<Button> vect;
vect.push_back(plusB);
this->buttonList.push_back(vect);
renderNotes();
}
我尝试过的:
将 this->addNote
替换为 stickyNotes::addNote
。
PS:
我不想让 addNote 成为静态的。我想保留它public,如何制作一个带有addNote回调函数的按钮?
如果有解决方法,我将很高兴听到。
问题:
成员函数只是 class 命名空间中的一个偏移量,因此您不能将偏移量作为绝对地址。
解决方案 1:
简单的方法是创建一个接口,在您的 class 中实现它,然后 return 或分配 this
作为接口。
解决方案 2:
其他选项是创建指向成员函数的指针。
例如:void (Class::*pfunc)()
将声明 pfunc
指针,它可以指向 class Class
的成员函数,如 void Class::Member()
解决方案 3:
正如@GoswinvonBrederlow 在评论中所建议的那样,使用 lambda 和 return 或将其分配为 std::function
:
std::function<void ()> Class::f()
{
return []() { /* do something...*/ };
}
您正在尝试将回调传递给按钮。如果您要求回调是一个简单的函数指针(不接收任何参数),那么您的回调不能保留任何 state.
但是,所有非静态成员函数都需要至少一个状态才能被调用:它们需要知道要使用哪个对象in/with。
这两者从根本上是不一致的:如果您的按钮采用简单的函数指针,则它不能用于调用成员函数(抛开全局状态等可怕的技巧)。
解决这个问题的一种方法是使用成员函数指针:它们指定它们在哪个 class 上运行(即是其成员函数)并且必须在该 class 的实例上调用。由于您可能希望按钮回调可用于多个 class,因此您可以使用多态性 - 这是 SHR 建议的界面解决方案。
按钮界面的另一个选项是获取(并存储)一个 std::function<void()>
而不是您的函数指针。 std::function
可以存储状态,与 lambda 函数一起使用非常方便:
Button plusB = makeButton(/* ... */, [this]() { addNote(); });
最后,您可以允许用户将一些 "custom data" 传递给按钮界面,可能作为传递给回调的 void*
或 std::any
。然后用户可以存储例如该自定义数据中的 stickyNotes
实例。这与简单的函数指针兼容。但是,这种方法规避了类型安全并且非常低级。它在某些库中使用,因为它非常通用,但使用起来比 std::function
方法更烦人。
我有 class stickyNotes
,其中有函数 addNote
,它是一个 public 非静态函数。
在代码中定义了一个新类型:
typedef void(*fptr)
我还有 class Button
,它在其构造函数中接受类型为 fptr
的变量,我有一个函数 makeButton()
returns a Button
对象。
stickyNotes
还有另一个名为 rendermainWindow
的成员,它呈现主 window 并添加一个按钮,我正在尝试创建一个 fptr
类型的新变量设置为 stickyNotes::addNote
的地址,我收到错误:
'&': illegal operation on bound member function expression
stickyNotes::rendermainWindow
:
void stickyNotes::rendermainWindow() {
/*
Renders the main window
*/
this->buttonList.empty(); // buttonList is a list of all buttons
mainWindow->clear(sf::Color(29, 29, 27, 255)); // clearing the window
sf::Vector2u windowDimensions = mainWindow->getSize(); // getting window dimensions
fptr cb = &this->addNote; <-------- ERROR HERE
Button plusB = makeButton((int)(windowDimensions.x * 0.75),
(int)(windowDimensions.y * 0.15),
(int)(windowDimensions.x * 0.85),
(int)(windowDimensions.y * 0.25),
mainWindow,
cb);
// first button to add stuff
std::vector<Button> vect;
vect.push_back(plusB);
this->buttonList.push_back(vect);
renderNotes();
}
我尝试过的:
将 this->addNote
替换为 stickyNotes::addNote
。
PS:
我不想让 addNote 成为静态的。我想保留它public,如何制作一个带有addNote回调函数的按钮? 如果有解决方法,我将很高兴听到。
问题:
成员函数只是 class 命名空间中的一个偏移量,因此您不能将偏移量作为绝对地址。
解决方案 1:
简单的方法是创建一个接口,在您的 class 中实现它,然后 return 或分配 this
作为接口。
解决方案 2:
其他选项是创建指向成员函数的指针。
例如:void (Class::*pfunc)()
将声明 pfunc
指针,它可以指向 class Class
的成员函数,如 void Class::Member()
解决方案 3:
正如@GoswinvonBrederlow 在评论中所建议的那样,使用 lambda 和 return 或将其分配为 std::function
:
std::function<void ()> Class::f()
{
return []() { /* do something...*/ };
}
您正在尝试将回调传递给按钮。如果您要求回调是一个简单的函数指针(不接收任何参数),那么您的回调不能保留任何 state.
但是,所有非静态成员函数都需要至少一个状态才能被调用:它们需要知道要使用哪个对象in/with。
这两者从根本上是不一致的:如果您的按钮采用简单的函数指针,则它不能用于调用成员函数(抛开全局状态等可怕的技巧)。
解决这个问题的一种方法是使用成员函数指针:它们指定它们在哪个 class 上运行(即是其成员函数)并且必须在该 class 的实例上调用。由于您可能希望按钮回调可用于多个 class,因此您可以使用多态性 - 这是 SHR 建议的界面解决方案。
按钮界面的另一个选项是获取(并存储)一个 std::function<void()>
而不是您的函数指针。 std::function
可以存储状态,与 lambda 函数一起使用非常方便:
Button plusB = makeButton(/* ... */, [this]() { addNote(); });
最后,您可以允许用户将一些 "custom data" 传递给按钮界面,可能作为传递给回调的 void*
或 std::any
。然后用户可以存储例如该自定义数据中的 stickyNotes
实例。这与简单的函数指针兼容。但是,这种方法规避了类型安全并且非常低级。它在某些库中使用,因为它非常通用,但使用起来比 std::function
方法更烦人。