2012-08-02 88 views
1

我試圖用metaprograming技術來創建一個編譯時位掩碼遞歸模板,我的想法是創建這樣的:了編譯時位掩碼

unsigned int Mask3 = Mask<2>(); // value = 0x03 = b00000000000000000000000000000011 
unsigned int Mask3 = Mask<3>(); // value = 0x07 = b00000000000000000000000000000111 
unsigned int Mask3 = Mask<7>(); // value = 0x7F = b00000000000000000000000001111111 

我想要的代碼是這樣的:

template <const unsigned int N> const unsigned int Mask() 
{ 
    if (N <= 1) 
    { 
     return 1; 
    } 
    else 
    { 
     return ((1 << N) | Mask<N - 1>()); 
    } 
} 

return 1;

但它導致噸雙警告:

  • 警告C4554: '< <':檢查運算符優先級爲可能出現的錯誤
  • 警告C4293: '< <':移位計數負或過大

而在最後,編譯錯誤:

  • 錯誤C1202:遞歸類型或函數依賴關係上下文過於複雜。

所以,我推斷遞歸永遠不會結束並落入編譯器無限循環,但我不理解爲什麼。

+0

爲什麼你不會像「int result = 0; for(i = 0; i SinisterMJ 2012-08-02 13:50:26

回答

4

正如已經指出的那樣,您要根據運行時檢查到 停止編譯時遞歸,這是行不通的。更重要的是, 也許,對於你想要做的是,你正在定義一個函數, ,直到你叫它沒有價值。所以即使在用專門化來停止 遞歸之後,仍然會有嵌套的 函數序列,這些函數將在運行時調用。

如果你想完整的編譯時間評估,你必須定義一個類模板的靜態數據 成員,因爲這是一個編譯時間 常量可以出現在模板中的唯一方法。喜歡的東西:

template <unsigned int N> 
struct Mask 
{ 
    static unsigned int const value = (1 << (N - 1)) | Mask<N - 1>::value; 
}; 

template <> 
struct Mask<0> 
{ 
    static unsigned int const value = 0; 
}; 

(我也糾正你有錯誤的數值)

當然,你什麼都不需要這個複雜的。下面 應該做的伎倆:

template <unsigned int N> 
struct Mask 
{ 
    static unsigned int const value = (1 << (N + 1)) - 1; 
}; 

template <> 
struct Mask<0> 
{ 
    static unsigned int const value = 0; 
}; 

(您仍然需要0。否則專業化,0表示所有位 集。)

最後,當然是:訪問該值,則需要寫點東西 就像Mask<3>::value。 (您可能希望在宏這個包起來。)

+0

作爲被接受的答案,僅僅是因爲解釋瞭如何在temeplate形式中公平而明確地做出解釋,但最好的答案是Sander De Dycker的答案。 – 2012-08-02 15:02:23

+2

除了來自Sander De Dycker的一個仍然涉及函數調用,所以值不是一個常量(然而,對於C++ 11,它可以被聲明爲'constexpr'。)並且它失敗了'N == 0'(否則,它對應於我的第二個解決方案,如上所示)。 – 2012-08-02 15:14:48

+0

如果N> = 32,我的編譯器抱怨說,它不符合標準,即使我把它做成'unsigned long long'。命令:**'clang ++ - 3.3 -Wall -Werror -std = C++ 11 -pedantic Mask.cpp ** (嚴格的C++ 11的標誌標準)結果:'Mask.cpp:7:48:錯誤:靜態數據的類內初始化器成員不是一個常量表達;將它摺疊成常量是GNU擴展[-Werror,-Wgnu]'**'static unsigned long long const value =(1 <<(N + 1)) - 1; ** ** Mask.cpp:19: 10:注意:在模板類'Mask <32>'在此處請求的實例化中**'cout << Mask<32> :: value << endl;'** – 2014-03-05 09:56:57

3

模板在編譯時創建,但您依賴運行時行爲來停止遞歸。

例如,如果你實例面膜< 2>,它是將要使用面膜< 1>,這是將要使用面膜< 0>,這是將要使用面膜< -1>等

你有一個運行時檢查N爲< = 1,但是這在編譯時沒有幫助。它仍然會創建無限的函數序列。

3

鈍模板實例遞歸您需要引入一個明確的分工:

template <0> const unsigned int Mask() 
{ 
    return 1; 
} 

你的遞歸永遠不會結束,因爲編譯器試圖生成模板實現兩個如果分支。所以當它生成Mask時它也會生成Mask < 0xffffffff等等

4

它不需要遞歸。這應該工作得很好:

template <const unsigned int N> const unsigned int Mask() 
{ 
    return ((1 << N) - 1); 
} 

它甚至不需要真的是一個模板。 (內聯)功能可以。

請注意,如果您要支持N的任何值,特別是N >= sizeof(unsigned int) * CHAR_BIT,那麼您可能希望將這些值作爲特例處理。

0

C++ 11 - 沒有遞歸或模板:

constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); } 
1

到目前爲止,答案只回答了第二個錯誤(C1202),但你問比那更多的。

警告C4554是由涉及模板參數和運算符的Microsoft編譯器錯誤引起的。因此,(1 < < N)會生成警告。如果N是一個普通的參數,當然不會有任何警告。

非常簡單的解決方法是使用(1 <(N))而不是(1 < < N),並且C4554消失!