小编典典

std::function 与模板

all

感谢
C++11,我们收到了std::function函子包装系列。不幸的是,我一直听到关于这些新增内容的坏消息。最受欢迎的是它们非常慢。我对其进行了测试,与模板相比,它们确实很糟糕。

#include <iostream>
#include <functional>
#include <string>
#include <chrono>

template <typename F>
float calc1(F f) { return -1.0f * f(3.3f) + 666.0f; }

float calc2(std::function<float(float)> f) { return -1.0f * f(3.3f) + 666.0f; }

int main() {
    using namespace std::chrono;

    const auto tp1 = system_clock::now();
    for (int i = 0; i < 1e8; ++i) {
        calc1([](float arg){ return arg * 0.5f; });
    }
    const auto tp2 = high_resolution_clock::now();

    const auto d = duration_cast<milliseconds>(tp2 - tp1);  
    std::cout << d.count() << std::endl;
    return 0;
}

111 毫秒与 1241 毫秒。我认为这是因为模板可以很好地内联,而functions 通过虚拟调用覆盖内部。

正如我所见,模板显然存在问题:

  • 它们必须作为标题提供,这不是您在将库作为封闭代码发布时可能不希望做的事情,
  • extern template除非引入类似策略,否则它们可能会使编译时间更长,
  • 没有(至少我知道)表达模板需求(概念,任何人?)的简洁方式,禁止评论描述预期的函子类型。

因此,我是否可以假设functions 可以用作传递函子的 事实上 的标准,并且在期望高性能的地方应该使用模板?


编辑:

我的编译器是没有 CTP的 Visual Studio 2012 。


阅读 74

收藏
2022-08-16

共1个答案

小编典典

一般来说,如果您面临的 设计 情况让您有选择的余地,请 使用模板 。我强调 设计
这个词是因为我认为你需要关注的是用例std::function和模板之间的区别,它们是非常不同的。

一般来说,模板的选择只是更广泛原则的一个实例: 尝试在编译时指定尽可能多的约束
。理由很简单:如果您可以在程序生成之前发现错误或类型不匹配,那么您就不会向您的客户发送有缺陷的程序。

此外,正如您正确指出的那样,对模板函数的调用是静态解析的(即在编译时),因此编译器具有优化并可能内联代码的所有必要信息(如果调用是通过虚表)。

是的,模板支持确实不完美,C++11还缺乏对概念的支持;但是,我不知道如何std::function在这方面拯救你。std::function不是模板的替代品,而是用于无法使用模板的设计情况的工具。

当您需要在运行时 通过调用遵循特定签名但其具体类型在编译时未知的可调用对象来解决调用时,就会出现一个这样的用例。当您有一组可能 不同类型
的回调,但您需要 统一调用
时,通常会出现这种情况;注册回调的类型和数量在运行时根据程序的状态和应用程序逻辑确定。其中一些回调可能是函子,一些可能是普通函数,一些可能是将其他函数绑定到某些参数的结果。

std::function并且std::bind还提供了一种在 C++ 中启用 函数式编程
的自然习惯用法,其中函数被视为对象并自然地柯里化并组合以生成其他函数。虽然这种组合也可以通过模板来实现,但类似的设计情况通常与需要在运行时确定组合的可调用对象的类型的用例一起出现。

最后,还有其他情况std::function是不可避免的,例如,如果你想编写递归
lambdas
;然而,我认为这些限制更多是由技术限制决定的,而不是由概念上的区别决定的。

总而言之, 专注于设计 并尝试了解这两种结构的概念用例是什么。如果你以你所做的方式将它们进行比较,你就是在强迫他们进入一个他们可能不属于的领域。

2022-08-16