在Stroustrup的The C++ programming language , Page 431中,当他讨论标准库的设计时,他说,
例如,将比较标准构建为排序函数是不可接受的,因为它是相同的 数据可以根据不同的标准进行排序。这就是为什么C标准库
qsort()
将比较函数作为参数而不是依赖于固定的东西,比如<运营商。另一方面,每次比较的函数调用所产生的开销会使qsort()
成为进一步构建库的构建块。
以上这些对我有意义。但在第二段中,他说,
这种开销严重吗?在大多数情况下,可能不是。但是,函数调用开销可以 主导某些算法的执行时间并使用户寻求替代方案。通过§13.4中描述的模板参数提供比较标准的技术解决了这个问题 问题。
在§13.4中,比较标准被定义为具有静态成员函数的类(进行比较)。当这些类用作模板参数时,仍然通过其静态成员函数进行比较。在我看来,仍然会有调用静态成员函数的开销。
Stroustrup的意思是什么?
答案 0 :(得分:8)
std::sort
是一个功能模板。在编译期间,将为每个类型和比较运算符创建单独的sort
实例。并且因为对于每个sort
实例化,类型和比较器在编译时是已知的,这允许内联比较器函数,因此避免了函数调用的成本。
答案 1 :(得分:3)
理论上没有sort
需要比qsort
更快的原因。有些编译器甚至会将函数指针内联到函数中,例如' qsort
:我相信我已经看过gcc或clang这样做(不是qsort
),甚至在功能定义位于不同的cpp文件中也是如此。
重要的是,sort
将函数对象作为类型和实例传递。生成每种类型的不同函数:template
s是函数的工厂。在调用它时,调用的确切函数对于每个这样的函数实例都很容易确定,因此内联很简单。
可以对函数指针执行相同的操作,但需要从调用qsort
的点开始内联,并仔细跟踪函数指针的不变性,并知道它是从哪个函数开始的。这在实践中比上述机制脆弱得多。
类似问题出现在元素步幅中(当sort
数组时显然是静态的,使用qsort
时更难处理)等。
答案 2 :(得分:1)
通过指针调用函数有两个开销:指针解除引用和函数调用开销。这是一个运行时过程。
模板实例化由编译器完成。指针解除引用被消除,因为显然没有指针。函数调用开销由编译器通过内联调用进行优化。