2012-01-21 65 views
3

請考慮下面的代碼:外部定義的函數指針作爲模板參數

main.cpp中:

#include <iostream> 
typedef void (* fncptr)(void); 
extern void externalfunc(void); 

template<void(* test)(void)> 
class Bar 
{ 
public: 
    Bar() { test(); } 
}; 

void localfunc() 
{ 
    std::cout << "Hello World" << std::endl; 
} 
constexpr fncptr alias = localfunc; 
extern fncptr externalAlias; 
int main(int argc, char* argv[]) 
{ 
    Bar<localfunc> b; 
    Bar<alias> b2; // This wouldn't compile if alias wasn't constexpr 
    Bar<externalfunc> b3; 
// Bar<externalAlias> b4; 

    return 0; 
} 

和external.cpp:

#include <iostream> 

typedef void (* fncptr)(void); 

void externalfunc() 
{ 
    std::cout << "Hello external world" << std::endl; 
} 

fncptr externalAlias = externalfunc; 

現在的問題是我需要的東西,如第四行主要功能工作。我從外部C庫中獲得這些函數聲明,因此我無法觸及這些函數聲明。目前第四行不能編譯。 gcc 4.6表示「它必須是具有外部連接功能的地址」。事實上,如果你使alias不是constexpr,那麼它也會這樣說,所以實際的含義(我認爲)應該被解釋爲:「我不知道100%確定你給我的函數地址是不變的,我需要用於實例化這個模板「。有沒有辦法解決這個問題,因爲我無法在main.cpp中聲明externalaliasconstexpr

在您使用替代解決方案之前:我已經試圖通過只通過構造函數傳遞函數指針並將它們保存在本地來獲得此工作,但如果我能使模板版本工作,我仍然感興趣。

+1

編譯器應該如何知道'externalAlias'的值是什麼,以便它可以實例化模板? –

+0

是的,它不知道'externalAlias'的值,但它是一個變量,因此它在編譯時知道該變量的位置,所以它可以在其上進行模板化。 (如另一個答案(即我的答案:-))所示)。 –

+0

你爲什麼想用模板來做課堂專業化?這就是繼承的原因。 (即test()應該只是一個純粹的虛擬方法Bar)然後在派生類中實現與localfunc,alias,externfunc等相對應的測試......或者更好的是,Bar只是一個獨立的類,在它的構造函數中傳遞了一個「ITest」類的實例。用模板做一些棘手的事情只會導致其他人不可繼承的代碼繼承。 – selbie

回答

2

名稱aliasexternAlias不是功能!它們是功能的指針,因此是可變的。您不能使用可變對象作爲模板參數,因爲模板參數需要在編譯時解析。但是,您可以執行以下兩項操作:

  1. 您可以使用您選擇的任何名稱爲包裝器創建一個調用可能的別名函數的包裝器。如果函數簽名之間存在某種程度的差異,這也可以方便地調整類型。
  2. 你可以使用一個常量指針作爲模板參數的函數指針:這裏也適用通常的一個更多級別的間接假設。

這一切都說,你究竟在努力實現什麼?你確定你不只是想要像std::function<void(void)>?我意識到,這是用於類型擦除,而你似乎有相反的目標,並將功能指針變成獨特的類型。然而,你的方式似乎並不是你打算做的。

下面是如何在第二個選項可能看起來像一個例子:

#include <iostream> 

extern void f(int); 
extern void g(int); 

void (*falias)(int) = f; 
void (*galias)(int) = g; 

template <void (**alias)(int)> 
struct foo 
{ 
    void bar() { (*alias)(17); } 
}; 

void f(int x) { std::cout << "f(x)=" << x << "\n"; } 
void g(int x) { std::cout << "g(x)=" << x << "\n"; } 

int main() 
{ 
    foo<&falias>().bar(); 
    foo<&galias>().bar(); 
} 

f()g()結束了在同一個翻譯單元正在實施的事實是完全無關緊要的:你可以在其他地方移動它們不任何問題。

+0

我正在使用一個類似於「createX,doSomethingWithX,deleteX」但具有不同X用途的庫函數,因爲這些調用的邏輯對於所有用途都是相同的,所以我試圖將它放在一個單獨的模板化容器中,將這3個函數指針傳遞給該模板,從而減少了爲每個X複製該功能的需要。類型對於每個X也應該是唯一的(由於一些靜態) – KillianDS

+0

爲什麼不能使用'&createX','&doSomethingWithX ','&deleteX'在這種情況下?這些似乎是函數庫中的函數,如果它們不是但可能是別名(即通過函數指針調用),您將不得不創建某種包裝...或使用選項2. –

+0

我試過它像你的例子一樣,它爲我的原始示例'localfunc'工作,但不''externalfunc'。但我可能會去選項1,並添加另一個包裝:)。 – KillianDS

1

如果您已準備好手動註釋這些類型,可以這樣做。最終,各種對象的類型是微妙不同的。

我會向後寫這個答案,看着main第一:

int main(int argc, char* argv[]) 
{ 
    Bar<void(*)(void) , localfunc> b; 
    Bar<void(**)(void), &alias> b2; 
    Bar<void(*)(void) , externalfunc> b3; 
    Bar<void(**)(void), &externalAlias> b4; 

    return 0; 
} 

你可能要考慮typeof(G ++)或decltype(C++ 11)創建稍微更具可讀性,也許使用宏:

Bar<decltype(&localfunc), localfunc> b; 
Bar<decltype(&alias), &alias> b2; 
Bar<decltype(&externalfunc), externalfunc> b3; 
Bar<decltype(&externalAlias), &externalAlias> b4; 

我不得不改變吧模板一點:

template<typename T, T t> 
class Bar; 

,以便它可以處理函數指針和指針函數指針。這需要兩個專業:

template<void(* test)(void)> 
class Bar<void(*)(void), test> 
{ 
public: 
    Bar() { std::cout << "* "; test(); } 
}; 
template<void(** test)(void)> 
class Bar<void(**)(void), test> 
{ 
public: 
    Bar() { std::cout << "**"; (*test)(); } 
}; 

應該可以添加更多專業化對於其他類型的功能有不同的參數類型和返回類型,甚至包括方法調用。

我在我自己的機器上完全測試了這一點。這是ideone上的演示(沒有外部功能)。

注意:使用別名時,模板參數是別名的地址,因此它將考慮到您在運行時對別名變量所做的任何更改。我想你可能會想讓別名爲const。 const fncptr alias = localfunc;