您描述的定义和调用函数的方式不是很安全。这是因为正如 Oli 在他们的评论中指出的那样,错误地调用函数很容易。如果目标函数需要 3 个参数,而你最终只传递了 1 个参数,那么你会得到未定义的行为,并且任何事情都可能发生。在这种情况下,“任何事情”都会是坏事。
如果您提前知道参数,您可以使用std::function
,std::bind
和参数的早期绑定以更安全的方式调用函数。这将允许您在 [应该]知道需要传递哪些参数、绑定参数然后设置的位置创建一个函数对象fooptr
。
下面的示例使用您问题中的代码,并使用std::bind
和std::function
以安全统一的方式调用函数对其进行扩展。它还包括一个使用std::reference_wrapper
以允许通过引用传递参数的示例。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <functional>
struct SDLSurface
{
std::string str;
};
struct Button
{
std::function<void(void)> fooptr;
};
void quit()
{
std::cout << "called quit()" << std::endl;
}
void print(SDLSurface *surface)
{
std::cout << "called print(\"" << surface->str << "\")" << std::endl;
}
void print_string(const std::string& str)
{
std::cout << "called print_string(\"" << str << "\")" << std::endl;
}
int main()
{
std::vector<std::unique_ptr<Button>> buttons;
std::unique_ptr<Button> b;
// Create a button and assign a function that takes no parameters
b.reset(new Button());
b->fooptr = quit;
buttons.push_back(std::move(b));
// Create a button and assign a function
b.reset(new Button());
SDLSurface surface;
b->fooptr = std::bind(print, &surface);
buttons.push_back(std::move(b));
// Create a button and assign a function taking a parameter by reference
b.reset(new Button());
std::string string_param;
// Since we are passing by reference and want to bind to an existing variable
// we need to use a reference wrapper.
b->fooptr = std::bind(
print_string,
std::reference_wrapper<const std::string>(string_param));
buttons.push_back(std::move(b));
// Call all functions setting the value of string_param before we do.
surface.str = "hello";
string_param = "world";
for(auto it = buttons.begin(); it != buttons.end(); ++it)
{
(*it)->fooptr();
}
}