2015-10-15 158 views
2

該函數的參數是特定類型的ID。該函數會將T中提供的值編碼爲標籤對應的類型的值,所以我想完成以下操作。有沒有什麼整潔的模板技巧來實現這個功能?可能的模板解決方案

template<int tag_id> 
struct tag_traits; 

template<> 
struct tag_traits<1> 
{ 
    typedef char type; 
}; 

template<> 
struct tag_traits<2> 
{ 
    typedef int type; 
}; 

template<> 
struct tag_traits<3> 
{ 
    typedef double type; 
}; 

class test { 
public: 
    test() 
    {} 

    template <typename T> 
    void add_field(int tag, T value) 
    { 
     using t = typename tag_traits<tag>::type; 
     // more... 
    }  
}; 

int main() 
{ 
    test t; 
    t.add_field(1, ""); 
    t.add_field(2, ""); 
} 

任何幫助,將不勝感激。

+1

做任何事情,從使用阻止你't.add_field <1>(「」);'到位t.add_field的'(1,「 「);'? –

+1

這是不可能的,因爲在編譯時標記是未知的。 – 0xBADF00

+0

在編譯時您需要模板參數,而add_field的參數在運行時給出。嘗試解釋目的是什麼?可能有更合適的解決方案。 – Taus

回答

3

在編譯時必須知道模板參數。如果你可以改變你的函數採取tag作爲模板參數,它會工作:

template <int tag, typename T> 
void add_field(T value) 
{ 
    using t = typename tag_traits<tag>::type; 
    // more... 
} 

// ... 

int main() 
{ 
    test t; 
    t.add_field<1>(""); 
    t.add_field<2>(""); 
} 

如果這不是一個選項,你需要一個switch或類似的結構:

template <typename T> 
void add_field(int tag, T value) 
{ 
    switch (tag) { 
    case 1: 
    { 
     using t = typename tag_traits<1>::type; 
     // more... 
     break; 
    } 
    case 2: 
    { 
     using t = typename tag_traits<2>::type; 
     // more... 
     break; 
    } 
    } 
} 

的問題是全部這些情況必須在語法上和語義上有效全部T。如果這是不是一種選擇,要麼,就需要有一個輔助特質重型機械模板:

template <int tag, class T> 
struct is_acceptable : std::false_type 
{}; 

template <int tag> 
struct is_acceptable<tag, typename tag_traits<tag>::type> : std::true_type 
{}; 

// Add any other specialisations as applicable 

// ... 
template <typename T> 
void add_field(int tag, T value) 
{ 
    switch (tag) { 
    case 1: 
    { 
     add_field_helper<1, T>::call(value); 
     break; 
    } 
    case 2: 
    { 
     add_field_helper<2, T>::call(value); 
     break; 
    } 
    } 
} 

template <int tag, class T, bool acceptable = is_acceptable<tag, T>::value> 
struct add_field_helper 
{ 
    static void call(T) {} 
}; 

template <int tag, class T> 
struct add_field_helper<tag, T, true> 
{ 
    static void call(T value) 
    { 
    using t = typename tag_traits<tag>::type; 
    // more... 
    } 
}; 

callis_acceptablefalse值的存在只是爲了避免編譯錯誤,不正確的實例。假設你永遠不會撥打add_field與無效的tag - T組合,它永遠不會在運行時調用。


如果有太多的標籤用手幾乎一一列舉了,你可以使用Boost.Preprocessor生成它們。對於有效標記值0 ... MAX_TAG - 1,下面可以使用:

#define ONE_CASE(z, tag, unused) \ 
    case tag: add_field_helper<tag, T>::call(value); break; 

template <typename T> 
void add_field(int tag, T value) 
{ 
    switch (tag) { 
    BOOST_PP_REPEAT(MAX_TAG, ONE_CASE, %%) 
    } 
} 

我沒有使用正常傳遞到宏觀,我通常使用句法廢話%%表示數據參數。如果你願意,你可以傳遞和使用任何東西。

如果提升。預處理是太黑暗魔法給你,你也可以使用模板遞歸:

template <class T> 
void add_field(int tag, T value) 
{ 
    add_field_dispatcher<0, T>::call(tag, value); 
} 

template <int one_tag, class T> 
struct add_field_dispatcher 
{ 
    static void call(int tag, T value) 
    { 
    if (tag == one_tag) add_field_helper<one_tag, T>::call(value); 
    else add_field_dispatcher<one_tag + 1, T>::call(tag, value); 
    } 
}; 

template <class T> 
struct add_field_dispatcher<MAX_TAG, T> 
{ 
    static void call(int tag, T value) 
    { 
    // Called with unsupported tag 
    } 
}; 
+0

由於可能有超過一百個標籤ID,因此「切換」解決方案並不是一種選擇。 – 0xBADF00

+0

@KaptenMugg我已經添加了兩個選項來自動生成'switch'(或類似構造)。 – Angew

+0

哇,這正是我想要的真棒,謝謝! – 0xBADF00

3
template <typename T> 
void add_field(int tag, T value) 
{ 
    using t = typename tag_traits<tag>::type; 
    // more... 
} 

tag_traits<tag>的參數需要是一個編譯時間常數,在這種情況下它不是。添加const也不能解決這種情況。

解決方法是將tag向上移動爲模板參數。

template <int tag, typename T> 
void add_field(T value) 
{ 
    using t = typename tag_traits<tag>::type; 
    // more... 
} 

另一種解決辦法,雖然有點更復雜的是使用一個tag分派技術 - 但是,在「調度」將是一個編譯時間計算。

如果要求是獲取運行時類型,那麼模板可能根本就不是答案,因爲模板必須編譯時間代碼生成。在這種情況下,變種和某種形式的訪問或更傳統的多態可能更合適。

+1

*「在這種情況下,constexpr可能很有用。」*,這裏「constexpr」有用嗎? –

+0

'constexpr'沒有用,我忘了它不能用於參數。 – Niall

+0

這不是一個解決方案,因爲在編譯時標記是未知的。 – 0xBADF00