2013-02-21 64 views
3

我有一個函數,它接受一個函數指針作爲參數,然後調用該函數有自己的論點:未通過指針常量數據作爲函數模板參數

typedef int (*my_func_ptr)(int); 

int foo(my_func_ptr f) { 
    static int i = 0; 
    return i = f(i); 
} 

有時候,我需要通過函數foo,它們不僅僅依賴整數輸入來輸出結果。

int add_strlen(int i, const char* s) { 
    return i + strlen(s); 
} 

我可以返工上面的代碼利用std::function然後用std::bind,但最好是對我來說,這些功能在編譯時創建的,所以我使用的模板。

template<const char* S> 
int add_strlen(int i) { 
    return i + strlen(S); 
} 

/** 
* Usage: 
* char bar[] = "bar"; 
* foo(add_strlen<bar>); 
*/ 

當使用指針作爲模板參數時,出現了我的問題。每當我使用指向任何類型的常量數據的指針作爲模板參數時,只要傳遞的參數被聲明爲該類型的非常量數組,它就只能編譯。

char char_array[]    = "works"; 
const char const_char_array[] = "error"; 
char *char_ptr    = "error"; 
const char *const_char_ptr = "error"; 

在Clang的相關錯誤(版本3.0-6)(於char_ptrconst_char_ptr錯誤是一樣的):

func_ptr.cpp:29:9: error: no matching function for call to 'foo' 
     foo(add_strlen<const_char_array>); 
     ^~~ 
func_ptr.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument 
int foo(my_func_ptr f) 

誰能給我解釋一下這是爲什麼?我看到它的方式,模板參數S預計將是const char*類型,在任何其他情況下,這意味着我可以傳入任何常量或非常量指針或類型爲char的數組,並期望它可以工作。我希望能夠將我的數組聲明爲const,因爲我甚至不想暗示它們是在運行時修改的。有沒有辦法讓我的數組const 將它們用作模板參數?

編輯:感謝一些幫助(和Clang的更新版本,更好的錯誤)我能夠確定提供一個模板參數與內部鏈接是問題的一部分。通過將上述變量聲明爲extern,我可以毫無錯誤地使用add_strlen<const_char_array>。我還創建了一個簡化的測試用例。它包括以下:


#include <cstring> 

typedef int (*my_func_ptr)(int); 

int foo(my_func_ptr f) { 
    static int i = 0; 
    return i = f(i); 
} 

template<const char* S> 
int add_strlen(int i) { 
    return i + strlen(S); 
} 

extern char char_array[]; 
extern const char const_char_array[]; 
extern char *char_ptr; 
extern const char *const_char_ptr; 

char char_array[] = "foo"; 
const char const_char_array[] = "bar"; 
// assigning to string literal is deprecated 
char *char_ptr = char_array; 
const char *const_char_ptr = "baz"; 

int main(int argc, const char *argv[]) 
{ 
    foo(add_strlen<char_array>); // works 
    foo(add_strlen<const_char_array>); // works 
    //foo(add_strlen<char_ptr>); // doesn't work 
    //foo(add_strlen<const_char_ptr>); // doesn't work 
    return 0; 
} 

回答

1

的錯誤似乎與你是什麼,他們有這樣說的你都不允許作爲非類型模板參數來使用,指的是IBM Linux Compilers documentation for Non-type template parameters

The syntax of a non-type template parameter is the same as a declaration of one of the following types:

  • integral or enumeration
  • pointer to object or pointer to function
  • reference to object or reference to function
  • pointer to member

爲什麼過去了char_array[]const_char_array[]工作的原因是因爲他們是在編譯時間常數,並在運行時將永遠程序下發生改變。整型可以傳遞,指針的整數類型但不能被通過。

模板期待一個類型的const char *又名const char[x],但它也期待的東西,永遠不會改變,這樣的位置在指針指向可能永遠不會改變。當在編譯器時間傳入const_char_array時,它將通過char[6](「錯誤」)。該位置永遠不會改變,內容永遠不會改變。然而,當傳遞const_char_ptr時,它會得到一個const char *,而指針本身可能永遠不會改變,它指向的位置可能會發生改變是完全可能的。它本身並不是靜態的。

char *_arr = new char[20]; 
const char* _ptr_arr = _arr; 

在這裏我們可以同意我的_ptr_arr是完全相同的類型,你const_char_ptr,但其中的內容存儲可以在運行時更改位置。在不允許使用的模板中,因爲它可能需要模板的全新實例化,並且在創建模板時不確定。 A char [6]是靜態的,不會改變。

foo(add_strlen<_ptr_arr>); 

導致以下編譯器錯誤:

test.cpp:36:5: error: no matching function for call to 'foo' 
    foo(add_strlen<_ptr_arr>); 
    ^~~ 
test.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument 
int foo(my_func_ptr f) { 
    ^

這是不是非常有幫助,我們想弄清楚爲什麼沒有有效的過載,編譯與函數的代碼獨立而不作爲一個函數指針傳遞我們得到如下:

add_strlen<_ptr_arr>(0); 

將導致:

test.cpp:36:5: error: no matching function for call to 'add_strlen' 
    add_strlen<_ptr_arr>(0); 
    ^~~~~~~~~~~~~~~~~~~~ 
test.cpp:16:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'S' 
int add_strlen(int i) { 
    ^

所以顯式指定的參數是無效的,具體地說,我們不能傳入一個指向積分的指針。