std::function 与模板 [英] std::function vs template

查看:20
本文介绍了std::function 与模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#include #include <功能>#include <字符串>#include <chrono>模板 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() {使用命名空间 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<毫秒>(tp2 - tp1);std::cout <<d.count() <

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

显然模板在我看来有其问题:

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

我可以因此假设 function 可以用作传递函子的事实上的标准,并且在应该使用高性能模板的地方吗?><小时>

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

解决方案

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

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

此外,正如您正确指出的那样,对模板函数的调用是静态解析的(即在编译时),因此编译器拥有优化和可能内联代码所需的所有信息(如果调用是通过 vtable 执行).

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

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

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

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

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

Thanks to C++11 we received the std::function family of functor wrappers. Unfortunately, I keep hearing only bad things about these new additions. The most popular is that they are horribly slow. I tested it and they truly suck in comparison with templates.

#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 ms vs 1241 ms. I assume this is because templates can be nicely inlined, while functions cover the internals via virtual calls.

Obviously templates have their issues as I see them:

  • they have to be provided as headers which is not something you might not wish to do when releasing your library as a closed code,
  • they may make the compilation time much longer unless extern template-like policy is introduced,
  • there is no (at least known to me) clean way of representing requirements (concepts, anyone?) of a template, bar a comment describing what kind of functor is expected.

Can I thus assume that functions can be used as de facto standard of passing functors, and in places where high performance is expected templates should be used?


Edit:

My compiler is the Visual Studio 2012 without CTP.

解决方案

In general, if you are facing a design situation that gives you a choice, use templates. I stressed the word design because I think what you need to focus on is the distinction between the use cases of std::function and templates, which are pretty different.

In general, the choice of templates is just an instance of a wider principle: try to specify as many constraints as possible at compile-time. The rationale is simple: if you can catch an error, or a type mismatch, even before your program is generated, you won't ship a buggy program to your customer.

Moreover, as you correctly pointed out, calls to template functions are resolved statically (i.e. at compile time), so the compiler has all the necessary information to optimize and possibly inline the code (which would not be possible if the call were performed through a vtable).

Yes, it is true that template support is not perfect, and C++11 is still lacking a support for concepts; however, I don't see how std::function would save you in that respect. std::function is not an alternative to templates, but rather a tool for design situations where templates cannot be used.

One such use case arises when you need to resolve a call at run-time by invoking a callable object that adheres to a specific signature, but whose concrete type is unknown at compile-time. This is typically the case when you have a collection of callbacks of potentially different types, but which you need to invoke uniformly; the type and number of the registered callbacks is determined at run-time based on the state of your program and the application logic. Some of those callbacks could be functors, some could be plain functions, some could be the result of binding other functions to certain arguments.

std::function and std::bind also offer a natural idiom for enabling functional programming in C++, where functions are treated as objects and get naturally curried and combined to generate other functions. Although this kind of combination can be achieved with templates as well, a similar design situation normally comes together with use cases that require to determine the type of the combined callable objects at run-time.

Finally, there are other situations where std::function is unavoidable, e.g. if you want to write recursive lambdas; however, these restrictions are more dictated by technological limitations than by conceptual distinctions I believe.

To sum up, focus on design and try to understand what are the conceptual use cases for these two constructs. If you put them into comparison the way you did, you are forcing them into an arena they likely don't belong to.

这篇关于std::function 与模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆