这两个函数原型有什么区别?
void apply1(double(f)(double));
void apply2(double(*f)(double));
如果目标是将提供的函数应用于数组,那么与其他版本相比是否有更快的版本?
编辑: 实施的一个例子:
#include <iostream>
#include <vector>
#include <cmath>
// First version
template<typename Type> void apply1(std::vector<Type>& v, Type(f)(Type))
{
for (unsigned int i = 0; i < v.size(); ++i) {
v[i] = f(v[i]);
}
}
// Second version
template<typename Type> void apply2(std::vector<Type>& v, Type(*f)(Type))
{
for (unsigned int i = 0; i < v.size(); ++i) {
v[i] = f(v[i]);
}
}
// Main
int main()
{
std::vector<double> v = {1., 2., 3., 4., 5.};
apply1(v, std::sin);
apply2(v, std::sin);
return 0;
}
答案 0 :(得分:5)
首先,模板包装器实例化的速度几乎完全取决于优化器。
那就是说,我已经将样本缩减为我能想到的最基本的代码,特别是检查函数参数的调用。你可以继续阅读,但你会发现它们完全相同。一项声明与另一项声明没有任何好处。此外,我包括你遗漏的那个,(参考文件)
#include <cstdio>
int hello(int x)
{
return x;
}
template<typename Type>
void apply1(Type x, Type (f)(Type))
{
f(x);
}
template<typename Type>
void apply2(Type x, Type (*f)(Type))
{
f(x);
}
template<typename Type>
void apply3(Type x, Type (&f)(Type))
{
f(x);
}
int main(int argc, char *argv[])
{
apply1(1,hello);
apply2(2,hello);
apply3(3,hello);
return 0;
}
扣除产生的实际asm是:
<强> apply1 强>
__Z6apply1IiEvT_PFS0_S0_E:
Leh_func_begin2:
pushq %rbp
Ltmp2:
movq %rsp, %rbp
Ltmp3:
subq $16, %rsp
Ltmp4:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
movl -4(%rbp), %ecx
movl %ecx, %edi
callq *%rax
addq $16, %rsp
popq %rbp
ret
Leh_func_end2:
<强> apply2 强>
__Z6apply2IiEvT_PFS0_S0_E:
Leh_func_begin3:
pushq %rbp
Ltmp5:
movq %rsp, %rbp
Ltmp6:
subq $16, %rsp
Ltmp7:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
movl -4(%rbp), %ecx
movl %ecx, %edi
callq *%rax
addq $16, %rsp
popq %rbp
ret
Leh_func_end3:
<强> apply3 强>
__Z6apply3IiEvT_RFS0_S0_E:
Leh_func_begin4:
pushq %rbp
Ltmp8:
movq %rsp, %rbp
Ltmp9:
subq $16, %rsp
Ltmp10:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
movl -4(%rbp), %ecx
movl %ecx, %edi
callq *%rax
addq $16, %rsp
popq %rbp
ret
Leh_func_end4:
他们相同(我怀疑他们会这样)。我无法看到无论。
注意:值得一提的是编译器通过名称修改检查看到这些声明的方式:
apply1: __Z6apply1IiEvT_PFS0_S0_E
apply2: __Z6apply2IiEvT_PFS0_S0_E
apply3: __Z6apply3IiEvT_RFS0_S0_E
答案 1 :(得分:5)
void apply1(double(f)(double));
void apply2(double(*f)(double));
这两个函数具有相同的签名,因此应该没有区别。它们都指向函数参数。
ISO / IEC 14882:2011 8.3.5 [dcl.fct] / 5:
在确定每个参数的类型后,任何类型为“T of T”或“函数返回T”的参数都将被调整为“指向T的指针”或“指向函数返回T的指针”。
答案 2 :(得分:1)
我会尽量让它更简单。让我们说有一个小程序:
#include <stdio.h>
void my_int_func(int x)
{
printf( "%d\n", x );
}
int main()
{
void (*foo)(int);
// the ampersand is actually optional
foo = &my_int_func;
return 0;
}
如上所示,有一个函数接受一个整数并返回一个void。 在main中我正在用my_int_func初始化一个函数指针foo。请仔细阅读评论“&符号是可选的”。 实际上它说全部。用一个&符号和没有&符号初始化没有区别。
所以你的两个陈述都没有区别。