所以我只是处理函数指针,我记得你可以这样做:
void Foo()
{
}
int main()
{
void(& func)() = Foo;
func(); //::Foo();
}
明显的优点是引用引用有效对象(除非它们被误用),或者在这种情况下是函数。
明显的缺点是你不能存储一个引用数组,并且不能将它们用于成员函数指针(至少据我所知)。
我的问题:有没有人使用它们(即函数引用,不是函数指针),如果有,在什么情况下有你发现它们有用/有用吗?
在使用条件编译时,唯一可以看到它们有用的地方是绑定对某个函数的引用。
答案 0 :(得分:13)
之前我已经使用它们通过以类似策略模式的方式将它们传递给构造函数来向类添加自定义
答案 1 :(得分:12)
我认为您的示例用法非常好。因为如果您使用普通函数指针,然后应用address-of运算符,您将获得函数指针的地址。使用对函数的引用将执行预期的操作,因为它返回指向函数本身的指针。
我也想不出很多例子。正如您所指出的,保持函数引用会产生一些难看的后果。另一个可能不需要的后果是,如果你作为一个类成员保存,如果你不编写自己的operator =你的对象将是不可赋值的,并且不要试图重新分配函数引用。
我认为函数引用的大多数用法都是隐式的,就像数组引用的大多数用法一样 - 尽管更多的是,当你通过引用接受参数时:
template<typename T>
void do_something(T const& t) { ... }
虽然通过引用接受数组具有不丢失其大小信息的优点,但通过引用明确接受函数似乎没有优势(至少就我所见)。我认为函数引用的存在在很大程度上是通过作为某个对象或函数的别名 -name的引用的理想主义视图来证明的,同时它允许将函数传递给接受它们的模板。参考参数。
如果我不可能不需要它们,我可能会避免使用它们。常量函数指针还提供不可重新分配的可调用对象,并且当其他可能不熟悉这种语言的程序员读取您的代码时,可能会避免混淆。值得注意的是Vandervoorde&amp; Josuttis还建议避免它们以减少混淆(在他们的书C ++模板 - 完整指南中)。
答案 2 :(得分:9)
与函数指针不同,函数引用使得从无效源创建它们变得更加困难。如果要围绕C库创建一个包装器,这很有用 - C ++代码可以通过引用获取回调函数,如果lbrary要求传递的指针不能为NULL,则将指针传递给C库。
它也是一种替代函数的便捷方法,尤其是在带有新auto关键字的C ++ 11中:
#include <iostream>
#include <typeinfo>
void f(int i, char c)
{
std::cout << i << ' ' << c << std::endl;
}
int main()
{
std::cout << typeid(f).name() << std::endl; //FvicE
f(0, '1');
void (*pf)(int, char) (&f); //ugly
std::cout << typeid(pf).name() << std::endl; //PFvicE
(*pf)(2, '3');
pf(4, '5'); //works, but I don't recommend it
void (&rf)(int, char) (f); //still ugly
std::cout << typeid(rf).name() << std::endl; //FvicE
rf(6, '7');
auto &af (f); //pretty, but only works in C++11
std::cout << typeid(af).name() << std::endl; //FvicE, same as above
af(8, '9');
}
答案 3 :(得分:2)
除了作为策略使用之外(正如Robert Gould指出的那样),我经常在(模板)元编程的入口处使用它们。可以通过模板参数轻松获取函数引用;从这一点开始,它可以通过几层(元编程)模板。当然,这对于函数指针也是如此,但引用是别名,因此更清楚地传达了意图。
举一个例子:在为应用程序编写通用命令调度系统时,需要将许多不同的操作声明为命令。我们可以使用简单的“构建器函数”作为客户端代码的前端。在幕后,这个构建器函数将实际的函数签名作为模板参数获取,导出(通过模板元编程)实际参数和返回类型值,并可能选择适当的特化来存储“memento”和“undo functor”。这些仿函数可以在内部存储为函数指针,也可以使用boost或tr1或C ++ 11函数对象存储。这样,就可以构建一个类型安全的命令调用和“撤销”系统。
答案 4 :(得分:1)
我在插件系统中使用过它们,在插件系统中可以在运行时加载/卸载插件DLL。我会在每个DLL中查找已知符号并将它们转换为函数指针。