2011-10-25 64 views
7

有了這個代碼:可變參數模板 - 不完全類型

template<class ...Args> 
struct Are_Same 
{ 
    enum {value = Are_Same<Args...>::value}; 
}; 

template<class A,class... C> 
struct Are_Same<A,C...> 
{ 
    enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE 
}; 

template<class A,class B> 
struct Are_Same<A,B> 
{ 
    enum {value = std::is_same<A,B>::value}; 
}; 

我從GCC 4.6.1收到錯誤:

error: incomplete type 'Are_Same' used in nested name specifier.

我認爲做Are_Same<A,C...>::value我將調用遞歸調用其在最後只會擴大到Are_Same<A,B>。顯然情況並非如此。任何人都知道我在哪裏犯錯誤?

回答

8

我認爲模板的定義是錯誤的,在這兩種情況下你都會觸發精確的遞歸。我本來期望的編譯器,而是產生了不同的錯誤,編譯器裏面的一些計算器死......

are_same可變參數模板的實現可能是:

template <class... Args>     // base (optional to declare the template) 
struct are_same; 

template <class A, class B, class... Args> // recursion 
struct are_same<A,B,Args...> { 
    static const bool value = is_same<A,B>::value && are_same<A,Args...>::value; 
}; 

template <class A, class B>     // stop condition 
struct are_same<A,B> { 
    static const bool value = is_same<A,B>::value; 
}; 

注意的是,在recursion步,從參數列表中刪除一個參數,以便解決的新問題是原始版本的減少版本。這種類型的模板元編程與遞歸非常相關,並且適用相同的規則,以便能夠使用您需要的遞歸來確保每個遞歸步驟使您更接近解決方案。在這種特殊情況下,給出一個N個潛在相同類型的列表,每個步驟都會減少問題,以找出N-1類型是否相同。

您也可以使用,如停止條件(替換前一個)are_same問題的退化版本:

template <class A> 
struct are_same<A> { 
    static const bool value = true; 
}; 

這是退化在這個意義上,它並沒有真正意義詢問是否一個類型* are_same *,但對於不同的元編程任務它可能是合適的。

不同的潛在的更有效的算法(我不知道編譯器是否會避免在上面的遞歸步驟的模板實例化),不依賴於is_same可能是:

template <class... Args> 
struct are_same; 

template <class A, class... Args> 
struct are_same<A,A,Args...> {    // recursion 
    static const bool value = are_same<A,Args...>::value; 
}; 

template <class A, class B, class... Args> 
struct are_same<A,B,Args...> {    // cut, A and B are not the same 
    static const bool value = false; 
}; 

template <class A> 
struct are_same<A> {      // end of recursion 
    static const bool value = true; 
}; 

在這種情況下, ,只要兩種類型相同,編譯器會更喜歡recursioncut步驟,所以我們不需要在內部檢查is_same。同時,如果編譯器進入cut步驟,我們不需要處理類型列表的其餘部分,因爲我們已經知道答案。

+1

這是一個驚人的輝煌,尤其是「短路」的一步。謝謝。 – smallB

+0

不錯,但是在第一個代碼片段中有一個小錯誤。應該是'static const bool value = is_same :: value && are_same :: value;' –

+1

@VJo:它們完全一樣,一旦您證明'A == B',與''或' ''''根本沒有關係,也沒有任何優勢,你觸發了一個新的模板實例,並且此時編譯器將解析'A'和'B'的類型(即int ','double' ...),這兩段代碼完全一樣。 –

3

我會做這樣的:

#include <type_traits> 
#include <iostream> 

template <class... Args> 
struct are_same 
{ 
    static const bool value=true; 
}; 

template <class A, class B, class... Args> // recursion 
struct are_same<A,B,Args...> { 
    static const bool value = std::is_same<A,B>::value && are_same<B,Args...>::value; 
}; 

int main() 
{ 
    std::cout<< std::boolalpha << are_same<int>::value << std::endl; 
    std::cout<< std::boolalpha << are_same< int, int, int >::value << std::endl; 
    std::cout<< std::boolalpha << are_same< int, int, double, int >::value << std::endl; 
} 
+0

謝謝我喜歡這種方式而不停止條件。好東西; +1 – smallB

+2

@smallB:這包含完全相同的停止條件,它只是隱藏在通用模板中,只有兩個專門化,一個用於通用步驟,一個用於停止條件(在這種情況下停止條件看起來更多*一般*,因爲它是'template '但它只會是編譯器的最佳匹配,因爲它不能再應用遞歸步驟(即只有一個類型)在我的情況下,我寧願讓停止條件更明確,因爲我討厭想想,並且必須弄清楚遞歸如何/何時停止,但這只是優先選擇。 –

0

也許最簡單的實現可能是這樣的:

template <typename... TList> 
struct are_same { constexpr static bool value = false; }; 

template <typename T, typename... TList> 
struct are_same<T, T, TList...> { 
    constexpr static bool value = are_same<T, TList...>::value; 
}; 

template <typename T> 
struct are_same<T> { constexpr static bool value = true; }; 

或者你可以用

template <typename T> 
struct are_same<T, T> { constexpr static bool value = true; }; 

但首先更換停止條件其中一個更通用,因爲are_same<type>::value == true。另一個問題是什麼應該是are_same<>::value等於。這給你false,但添加一個像這樣的模板專門化並不是什麼大問題。

template <> 
struct are_same<> { constexpr static bool value = true; };