2016-08-26 43 views
2

編輯:問題解決了,這只是錯誤的函數聲明順序。遞歸模板調用好奇地不可能與SFINAE

當試圖解決一個乍一看似乎很瑣碎的問題時,我偶然發現了我無法解釋的行爲。

我想處理一個任意的int數組遞歸,當然我必須以某種方式停止遞歸。 作爲偏特以具體的數字是不可能的模板函數(如

template<typename T, int N> foo<T, 0>(void) 

),我想這個假貨與SFINAE。但是當我想從第一個調用第二個SFNIAE函數時,我得到一個編譯器錯誤。 完整的代碼示例:

#include <algorithm> 
#include <iostream> 

using namespace std; 

// ------------------------------------------------------------- 
template<typename T, int N> 
void foo(T const& param, typename enable_if<N != 0, int>::type* = 0) 
{ 
    cout << "recursive step " << N << endl; 

    /* --- This was, what I desired: --- */ 
    //foo<T, N - 1>(param); 

    /* --- THIS IS CAUSING AN ERROR! --- */ 
    foo<T, 0>(param); 
} 

// ------------------------------------------------------------- 
template<typename T, int N> 
void foo(T const& param, typename enable_if<N == 0, int>::type* = 0) 
{ 
    cout << "finish recursion" << endl; 
} 


// ============================================================= 
int main() 
{ 
    int a[5] = {0, 1, 2, 3, 4}; 
    foo<decltype(a), 5>(a); 

    /* --- SAME CALL AS WITHIN foo(), BUT CAUSING NO ERROR! --- */ 
    foo<decltype(a), 0>(a); 
} 

編譯器告訴我: 的main.cpp:9:Fehler:無型「結構的std :: enable_if」命名的「類型」 這麼看來他莫名其妙解決不了的第二個功能。 但是,如果我從main()調用函數,它不是問題。

這是我第一次和SFINAE合作,我希望我沒有犯任何微小的錯誤。 感謝大家讀了這麼多!

+0

切換兩個定義 - 第一個定義沒有看到第二個定義,但您試圖調用它(小事件必須高於正常情況)。 – Holt

回答

1

我與Clang's helpfulness在這一個驚喜:

main.cpp:14:5: error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup 
    foo(param); 
    ^
main.cpp:29:5: note: in instantiation of function template specialization 'foo' requested here 
    foo(a); 
    ^
main.cpp:19:6: note: 'foo' should be declared prior to the call site 
void foo(T const&, typename std::enable_if::type* = 0) 
    ^

我的意思是,這只是一個步驟就可以在Stack Overflow上登錄,並給予答案本身。無論如何。

添加上述第一個第二個函數的聲明,所以你可以從其中調用它:

template<typename T, int N> 
void foo(T const&, typename std::enable_if<N == 0, int>::type* = 0); 

請注意,您必須從定義中刪除默認參數爲好。

+0

謝謝。但我不得不問,爲什麼你建議刪除默認參數?它只是重新安排功能。但是當我刪除= 0時,不是合理的。 – Shredd0r

+0

@ Shredd0r我建議添加一個聲明,而不是重新排序函數。兩者都有效(因爲定義也是一種聲明),但在某些情況下重新排序函數可能是不可能的。然後,只有第一個聲明必須指定一個默認參數。 – Quentin

1

你需要切換定義的順序(或在正常情況下,之前宣佈這個瑣碎的例子),正常情況下不會看到微不足道的,因此不能稱之爲:

template<typename T, int N> 
void foo(T const& param, typename enable_if<N == 0, int>::type* = 0) { 
    cout << "finish recursion" << endl; 
} 

template<typename T, int N> 
void foo(T const& param, typename enable_if<N != 0, int>::type* = 0) { 
    cout << "recursive step " << N << endl; 
    foo<T, N - 1>(param); 
} 

注意,你可以使用一個輔助類在這裏爲了避免這種enable_if(,改變它的簽名,這樣你可以讓編譯器推斷參數):

template<typename T, int N> 
struct foo_h { 
    static void call(T const& param) { 
     foo_h<T, N - 1>::call(param); 
    } 
}; 


template<typename T> 
struct foo_h<T, 0> { 
    static void call(T const& param) { 

    } 
}; 

template<typename T, int N> 
void foo(const T (&param)[N]) { 
    foo_h<const T[N], N>::call(param); 
} 

然後:

int arr[] = {1, 2, 3, 4, 5}; 

foo(arr); // Automatic template parameters deduction!