仿函数调用和函数调用的详细区别?

发布于 2024-09-27 09:20:35 字数 502 浏览 8 评论 0原文

这有效的关键原因是 for_each () 实际上并没有假设 它的第三个参数是一个函数。 它只是假设它的第三个 论证是可以的 用适当的参数调用。一个 适当定义的对象也可以 作为——而且往往比——更好 功能。例如,更容易 内联应用程序运算符 类而不是内联传递的函数 作为函数指针。 因此,函数对象经常 比普通人执行得更快 功能。一个类的对象 应用程序运营商(§11.9)是 称为类函数对象, 函子,或者简称为函数对象。

[Stroustrup,C++ 第三版,18.4-最后一段]

  1. 我一直认为运算符 ( ) 调用就像函数调用 在运行时。它与什么不同 正常的函数调用?

  2. 为什么内联更容易 应用程序操作员比普通操作员 函数?

  3. 它们如何比函数更快 调用?

The key reason this works is that for_each () doesn’t actually assume
its third argument to be a function.
It simply assumes that its third
argument is something that can be
called with an appropriate argument. A
suitably defined object serves as well
as – and often better than – a
function. For example, it is easier to
inline the application operator of a
class than to inline a function passed
as a pointer to function.
Consequently, function objects often
execute faster than do ordinary
functions. An object of a class with
an application operator (§11.9) is
called a functionlike object, a
functor, or simply a function object.

[Stroustrup, C++ 3rd edition, 18.4-last paragraph]

  1. I always thought that the operator
    ( )
    call is just like function call
    at runtime. how does it differ from
    a normal function call?

  2. Why is it easier to inline the
    application operator than a normal
    function?

  3. How are they faster than function
    call?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

演出会有结束 2024-10-04 09:20:35

一般来说,函子被传递给模板化函数 - 如果你这样做,那么传递“真实”函数(即函数指针)或函子(即类)并不重要使用重载的 operator())。本质上,两者都有一个函数调用运算符,因此都是有效的模板参数,编译器可以为其实例化 for_each 模板。 这意味着 for_each 要么使用传递的函子的特定类型进行实例化,要么使用传递的函数指针的特定类型进行实例化。< /strong> 正是在这种专业化中,函子有可能胜过函数指针。

毕竟,如果您传递函数指针,那么参数的编译类型就是函数指针。如果 for_each 本身没有内联,那么这个特定的 for_each 实例将被编译为调用不透明的函数指针 - 毕竟,编译器如何内联函数指针?它只知道它的类型,而不是实际传递了该类型的哪个函数 - 至少,除非它在优化时可以使用非本地信息,而这是很难做到的。

但是,如果您传递函子,则该函子的编译时类型将用于实例化for_each 模板。这样做时,您可能会传递一个简单的非虚拟类,其中只有一个适当 operator() 的实现。因此,当编译器遇到对operator()的调用时,它确切地知道意味着哪个实现——该函子的唯一实现——现在它可以内联它。

如果您的函子使用虚方法,则潜在优势就会消失。当然,函子是一个类,您可以用它来做各种其他低效的事情。但对于基本情况,这就是为什么编译器更容易优化和优化的原因。内联函子调用而不是函数指针调用。

摘要

函数指针无法内联,因为在编译 for_each 时,编译器只有以下类型:
函数而不是函数的恒等式。相比之下,函子可以内联,因为即使编译器只有函子的类型,该类型通常足以唯一标识函子的 operator() 方法。

Generally, functors are passed to templated functions - if you're doing so, then it doesn't matter if you pass a "real" function (i.e. a function pointer) or a functor (i.e. a class with an overloaded operator()). Essentially, both have a function call operator and are thus valid template parameters for which the compiler can instantiate the for_each template. That means for_each is either instantiated with the specific type of the functor passed, or with the specific type of function pointer passed. And it's in that specialization that it is possible for functors to outperform function pointers.

After all, if you're passing a function pointer, then the compile-type type of the argument is just that - a function pointer. If for_each itself is not inlined, then this particular for_each instance is compiled to call an opaque function pointer - after all, how could the compiler inline a function pointer? It just knows its type, not which function of that type is actually passed - at least, unless it can use non-local information when optimizing, which is harder to do.

However, if you pass a functor, then the compile-time type of that functor is used to instantiate the for_each template. In doing so, you're probably passing a simple, non-virtual class with only one implementation of the appropriate operator(). So, when the compiler encounters a call to operator() it knows exactly which implementation is meant - the unique implementation for that functor - and now it can inline that.

If your functor uses virtual methods, the potential advantage disappears. And, of course, a functor is a class with which you can do all kinds of other inefficient things. But for the basic case, this is why it's easier for the compiler to optimize & inline a functor call than a function pointer call.

Summary

Function pointers can't be inlined since while compiling for_each the compiler has only the type of
the function and not the identity of the function. By contrast, functors can be inlined since even though the compiler only has the type of functor, the type generally suffices to uniquely identify the functor's operator() method.

半枫 2024-10-04 09:20:35

考虑以下两个模板实例化:

std::for_each<class std::vector<int>::const_iterator, class Functor>(...)

并且

std::for_each<class std::vector<int>::const_iterator, void(*)(int)>(...)

因为第一个是为每种类型的函数对象定制的,并且因为 operator() 通常是内联定义的,所以编译器可以自行决定选择内联称呼。

在第二种情况下,编译器将为具有相同签名的所有函数实例化模板一次,因此,它无法轻松内联调用。

现在,智能编译器也许能够确定在编译时调用哪个函数,尤其是在这样的场景中:

std::for_each(v.begin(), v.end(), &foo);

并且仍然通过生成自定义实例化而不是前面提到的单个通用实例来内联函数。

Consider the two following template instantiations:

std::for_each<class std::vector<int>::const_iterator, class Functor>(...)

and

std::for_each<class std::vector<int>::const_iterator, void(*)(int)>(...)

Because the 1st is customised for each type of function object, and because operator() is often defined inline, then the compiler may, at its discretion, choose to inline the call.

In the 2nd scenario, the compiler will instantiate the template once for all functions of the same signature, therefore, it cannot easily inline the call.

Now, smart compilers may be able to figure out which function to call at compile time, especially in scenarios like this:

std::for_each(v.begin(), v.end(), &foo);

and still inline the function by generating custom instantiations instead of the single generic one mentioned earlier.

半边脸i 2024-10-04 09:20:35

我一直认为operator()调用就像运行时的​​函数调用一样。它与普通函数调用有何不同?

我的猜测不是很大。要获得这方面的证据,请查看每个编译器的汇编输出。假设优化级别相同,它可能几乎相同。 (必须传递 this 指针的附加细节。)

为什么内联应用程序运算符比普通函数更容易?

引用您引用的简介:

例如,内联类的应用程序运算符比内联作为函数指针传递的函数更容易。

我不是编译器人员,但我将其理解为:如果通过函数指针调用该函数,则编译器很难猜测该函数指针中存储的地址是否会在运行时更改,因此它不是可以安全地将 call 指令替换为函数体;想想看,函数本身的主体在编译时不一定是已知的。

它们如何比函数调用更快?

在许多情况下,我希望您不会注意到任何差异。但是,考虑到您引用的论点,即编译器可以自由地进行更多内联,这可能会产生更好的代码局部性 和更少的分支。如果频繁调用该代码,这将产生显着的加速。

I always thought that the operator ( ) call is just like function call at runtime. how does it differ from a normal function call?

My guess is not very much. For evidence of this, look at your compiler's assembly output for each. Assuming the same level of optimization, it's likely to be nearly identical. (With the additional detail that the this pointer will have to get passed.)

Why is it easier to inline the application operator than a normal function?

To quote the blurb you quoted:

For example, it is easier to inline the application operator of a class than to inline a function passed as a pointer to function.

I am not a compiler person, but I read this as: If the function is being called through a function pointer, it's a hard problem for the compiler to guess whether the address stored in that function pointer will ever change at runtime, therefore it's not safe to replace the call instruction with the body of the function; come to think of it, the body of the function itself wouldn't necessarily be known at compile time.

How are they faster than function call?

In many circumstances I'd expect you wouldn't notice any difference. But, given your quotation's argument that the compiler is free to do more inlining, this could produce better code locality and fewer branches. If the code is called frequently this would produce notable speedup.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文