3

这两个函数原型有什么区别?

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;
}
4

3 回答 3

5

First, the speed of the template wrapper instantiation is going to be almost entirely at the mercy of your optimizer.

That said, I've reduced your samples to the most basic code I can think of, specifically to check the invoke of the function parameters. You can read on, but you'll see they invoke exactly the same. There is no benefit for one declaration vs another. Further, I included the one you left out, (reference-decl)

#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;
}

The actual asm generated from the deductions is:

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:

They are identical (as I suspected they would be). There is no difference that I can see whatsoever.

Note: it is worth mentioning the way the compiler saw these declarations by name mangling examination:

apply1: __Z6apply1IiEvT_PFS0_S0_E
apply2: __Z6apply2IiEvT_PFS0_S0_E
apply3: __Z6apply3IiEvT_RFS0_S0_E
于 2012-11-05T06:51:03.817 回答
5
void apply1(double(f)(double));
void apply2(double(*f)(double));

这两个函数具有相同的签名,因此应该没有区别。它们都采用指向函数参数的指针。

ISO/IEC 14882:2011 8.3.5 [dcl.fct] / 5:

在确定每个参数的类型后,将“T的数组”或“返回T的函数”类型的任何参数分别调整为“指向T的指针”或“返回T的函数的指针”。

于 2012-11-05T07:28:40.083 回答
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。请仔细查看注释“& 符号是可选的”。实际上它说明了一切。用 & 号初始化和没有 & 号初始化没有区别。

所以你的两个陈述没有区别。

于 2012-11-05T07:02:51.697 回答