2012-11-05 65 views
3

這兩個函數原型有什麼區別?函數指針的類型(f)(類型)和類型(* f)(類型)之間的區別?

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

現在你的函數聲明不符合定義。你正在離開std :: vector參數。第二個聲明是唯一具有有效函數指針的聲明。 –

+0

[函數指針](http://www.cprogramming.com/tutorial/function-pointers.html) –

+0

好的,那麼爲什麼第一個版本在示例中編譯? – Vincent

回答

5

首先,模板包裝實例化的速度將是幾乎完全由您優化的擺佈。這就是說,我已經將你的示例縮減爲我能想到的最基本的代碼,特別是檢查函數參數的調用。你可以閱讀,但你會看到他們調用完全相同。一個聲明對另一個聲明沒有好處。此外,我包括你離開出一個,(參考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; 
} 

從扣除所產生的實際的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

我會盡量保持它更簡單地說you.Lets有一個小節目:

#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。 主內我正在用my_int_func初始化一個函數指針foo。請仔細看看註釋「和號是可選的」。 其實它說all.there沒有任何區別初始化與&符號,沒有&符號。

所以在你的兩個陳述中沒有區別。

+0

附註:在處理函數指針時,操作符(&符號)的地址通常被認爲是更好的樣式,因爲當您使用&func時,讀者將分配函數指針變得更加清晰,而不僅僅是獲取一個被調用函數。但更重要的是,它保持語言符號的連貫性,因爲您會將指向對象和指針的指針以相同的方式(語法方式)進行操作。 – Lundin

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的函數,「 分別。