我应该在 C++ 中使用 std::function 还是函数指针?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25848690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should I use std::function or a function pointer in C++?
提问by Jan Swart
When implementing a callback function in C++, should I still use the C-style function pointer:
在 C++ 中实现回调函数时,我是否仍应使用 C 风格的函数指针:
void (*callbackFunc)(int);
Or should I make use of std::function:
或者我应该使用 std::function :
std::function< void(int) > callbackFunc;
回答by leemes
In short, use std::function
unless you have a reason to not.
简而言之,std::function
除非您有理由不使用,否则请使用。
Function pointers have the disadvantage of not being able to capturesome context. You won't be able to for example pass a lambda function as a callback which captures some context variables (but it will work if it doesn't capture any). Calling a member variable of an object (i.e. non-static) is thus also not possible, since the object (this
-pointer) needs to be captured.(1)
函数指针的缺点是无法捕获某些上下文。例如,您将无法将 lambda 函数作为捕获某些上下文变量的回调传递(但如果它不捕获任何上下文变量,它将起作用)。因此,调用对象(即非静态)的成员变量也是不可能的,因为this
需要捕获对象(-pointer)。(1)
std::function
(since C++11) is primarily to storea function (passing it around doesn't require it to be stored). Hence if you want to store the callback for example in a member variable, it's probably your best choice. But also if you don't store it, it's a good "first choice" although it has the disadvantage of introducing some (very small) overhead when being called (so in a very performance-critical situation it might be a problem but in most it should not). It is very "universal": if you care a lot about consistent and readable code as well as don't want to think about every choice you make (i.e. want to keep it simple), use std::function
for every function you pass around.
std::function
(从 C++11 开始)主要是存储一个函数(传递它不需要存储它)。因此,如果您想将回调存储在例如成员变量中,这可能是您的最佳选择。但是如果你不存储它,它也是一个很好的“首选”,尽管它的缺点是在调用时引入一些(非常小的)开销(所以在非常关键的情况下它可能是一个问题,但在大多数情况下)它不应该)。它非常“通用”:如果您非常关心一致和可读的代码,并且不想考虑您所做的每一个选择(即希望保持简单),请std::function
为您传递的每个函数使用。
Think about a third option: If you're about to implement a small function which then reports something via the provided callback function, consider a template parameter, which can then be any callable object, i.e. a function pointer, a functor, a lambda, a std::function
, ... Drawback here is that your (outer) function becomes a template and hence needs to be implemented in the header. On the other hand you get the advantage that the call to the callback can be inlined, as the client code of your (outer) function "sees" the call to the callback will the exact type information being available.
考虑第三种选择:如果您要实现一个小函数,然后通过提供的回调函数报告某些内容,请考虑模板参数,它可以是任何可调用对象,即函数指针、函子、lambda, a std::function
, ... 这里的缺点是您的(外部)函数变成了模板,因此需要在标头中实现。另一方面,您可以获得可以内联回调调用的优势,因为(外部)函数的客户端代码“看到”回调调用将获得可用的确切类型信息。
Example for the version with the template parameter (write &
instead of &&
for pre-C++11):
带有模板参数的版本示例(写入&
而不是&&
C++11 之前的版本):
template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}
As you can see in the following table, all of them have their advantages and disadvantages:
如下表所示,它们各有优缺点:
+-------------------+--------------+---------------+----------------+
| | function ptr | std::function | template param |
+===================+==============+===============+================+
| can capture | no(1) | yes | yes |
| context variables | | | |
+-------------------+--------------+---------------+----------------+
| no call overhead | yes | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be inlined | no | no | yes |
| (see comments) | | | |
+-------------------+--------------+---------------+----------------+
| can be stored | yes | yes | no(2) |
| in class member | | | |
+-------------------+--------------+---------------+----------------+
| can be implemented| yes | yes | no |
| outside of header | | | |
+-------------------+--------------+---------------+----------------+
| supported without | yes | no(3) | yes |
| C++11 standard | | | |
+-------------------+--------------+---------------+----------------+
| nicely readable | no | yes | (yes) |
| (my opinion) | (ugly type) | | |
+-------------------+--------------+---------------+----------------+
(1) Workarounds exist to overcome this limitation, for example passing the additional data as further parameters to your (outer) function: myFunction(..., callback, data)
will call callback(data)
. That's the C-style "callback with arguments", which is possible in C++ (and by the way heavily used in the WIN32 API) but should be avoided because we have better options in C++.
(1) 存在克服此限制的变通方法,例如将附加数据作为进一步参数传递给您的(外部)函数:myFunction(..., callback, data)
将调用callback(data)
. 这是 C 风格的“带参数回调”,这在 C++ 中是可能的(顺便说一下,在 WIN32 API 中大量使用)但应该避免,因为我们在 C++ 中有更好的选择。
(2) Unless we're talking about a class template, i.e. the class in which you store the function is a template. But that would mean that on the client side the type of the function decides the type of the object which stores the callback, which is almost never an option for actual use cases.
(2) 除非我们谈论的是类模板,即您存储函数的类是模板。但这意味着在客户端,函数的类型决定了存储回调的对象的类型,这几乎不是实际用例的选项。
(3) For pre-C++11, use boost::function
(3) 对于 C++11 之前的版本,使用 boost::function
回答by Yakk - Adam Nevraumont
void (*callbackFunc)(int);
may be a C style callback function, but it is a horribly unusable one of poor design.
void (*callbackFunc)(int);
可能是一个 C 风格的回调函数,但它是一个糟糕的设计,非常无法使用。
A well designed C style callback looks like void (*callbackFunc)(void*, int);
-- it has a void*
to allow the code that does the callback to maintain state beyond the function. Not doing this forces the caller to store state globally, which is impolite.
一个设计良好的 C 风格回调看起来像void (*callbackFunc)(void*, int);
——它void*
允许执行回调的代码在函数之外保持状态。不这样做会强制调用者全局存储状态,这是不礼貌的。
std::function< int(int) >
ends up being slightly more expensive than int(*)(void*, int)
invokation in most implementations. It is however harder for some compilers to inline. There are std::function
clone implementations that rival function pointer invokation overheads (see 'fastest possible delegates' etc) that may make their way into libraries.
std::function< int(int) >
int(*)(void*, int)
在大多数实现中,最终比调用稍微贵一些。然而,一些编译器更难内联。有一些std::function
克隆实现可以与函数指针调用开销相媲美(参见“最快的委托”等),它们可能会进入库。
Now, clients of a callback system often need to set up resources and dispose of them when the callback is created and removed, and to be aware of the lifetime of the callback. void(*callback)(void*, int)
does not provide this.
现在,回调系统的客户端通常需要在创建和删除回调时设置资源并处理它们,并了解回调的生命周期。 void(*callback)(void*, int)
不提供这个。
Sometimes this is available via code structure (the callback has limited lifetime) or through other mechanisms (unregister callbacks and the like).
有时这可以通过代码结构(回调的生命周期有限)或其他机制(取消注册回调等)获得。
std::function
provides a means for limited lifetime management (the last copy of the object goes away when it is forgotten).
std::function
提供了一种有限生命周期管理的方法(对象的最后一个副本在被遗忘时消失)。
In general, I'd use a std::function
unless performance concerns manifest. If they did, I'd first look for structural changes (instead of a per-pixel callback, how about generating a scanline processor based off of the lambda you pass me? which should be enough to reduce function-call overhead to trivial levels.). Then, if it persists, I'd write a delegate
based off fastest possible delegates, and see if the performance problem goes away.
一般来说,我会使用 astd::function
除非性能问题明显。如果他们这样做了,我会首先寻找结构变化(而不是每像素回调,如何根据您传递给我的 lambda 生成扫描线处理器?这应该足以将函数调用开销减少到微不足道的水平。 )。然后,如果它仍然存在,我会写一个delegate
基于最快的委托,看看性能问题是否消失。
I would mostly only use function pointers for legacy APIs, or for creating C interfaces for communicating between different compilers generated code. I have also used them as internal implementation details when I am implementing jump tables, type erasure, etc: when I am both producing and consuming it, and am not exposing it externally for any client code to use, and function pointers do all I need.
我通常只会将函数指针用于遗留 API,或者用于创建 C 接口以在不同编译器生成的代码之间进行通信。当我实现跳转表、类型擦除等时,我也将它们用作内部实现细节:当我同时生成和使用它,并且没有将它暴露给任何客户端代码使用时,函数指针做我需要的一切.
Note that you can write wrappers that turn a std::function<int(int)>
into a int(void*,int)
style callback, assuming there are proper callback lifetime management infrastructure. So as a smoke test for any C-style callback lifetime management system, I'd make sure that wrapping a std::function
works reasonably well.
请注意,假设有适当的回调生命周期管理基础结构,您可以编写将 astd::function<int(int)>
转换为int(void*,int)
样式回调的包装器。因此,作为任何 C 风格回调生命周期管理系统的冒烟测试,我会确保包装 astd::function
工作得相当好。
回答by Mike Seymour
Use std::function
to store arbitrary callable objects. It allows the user to provide whatever context is needed for the callback; a plain function pointer does not.
使用std::function
存储任意调用对象。它允许用户提供回调所需的任何上下文;一个普通的函数指针没有。
If you do need to use plain function pointers for some reason (perhaps because you want a C-compatible API), then you should add a void * user_context
argument so it's at least possible (albeit inconvenient) for it to access state that's not directly passed to the function.
如果出于某种原因确实需要使用普通函数指针(可能是因为您想要一个与 C 兼容的 API),那么您应该添加一个void * user_context
参数,以便它至少可以(尽管不方便)访问未直接传递给功能。
回答by dasblinkenlight
The only reason to avoid std::function
is support of legacy compilers that lack support for this template, which has been introduced in C++11.
唯一需要避免的原因std::function
是支持缺乏对 C++11 中引入的此模板的支持的旧编译器。
If supporting pre-C++11 language is not a requirement, using std::function
gives your callers more choice in implementing the callback, making it a better option compared to "plain" function pointers. It offers the users of your API more choice, while abstracting out the specifics of their implementation for your code that performs the callback.
如果不需要支持 C++11 之前的语言,则 usingstd::function
为您的调用者提供了更多实现回调的选择,使其成为与“普通”函数指针相比更好的选择。它为您的 API 用户提供了更多选择,同时为执行回调的代码抽象出他们实现的细节。
回答by vladon
std::function
may bring VMT to the code in some cases, which has some impact on performance.
std::function
在某些情况下可能会给代码带来 VMT,这对性能有一定的影响。