2015-09-18 11 views
2

我已經能夠從我的previous question我的variadic模板進一步進步。我現在有了一個新問題。在此代碼示例:C++變量模板刪除函數邏輯

#include <iostream> 
#include <cstddef> 

constexpr std::uint32_t Flag0 = 0x0001; 
constexpr std::uint32_t Flag1 = 0x0002; 
constexpr std::uint32_t Flag2 = 0x0004; 
constexpr std::uint32_t FlagAll = 0xFFFF; 

template<std::uint32_t...Cs> 
struct flags_tag {constexpr flags_tag(){}; }; 

template<std::uint32_t...Cs> 
struct make_flags{ using type=flags_tag<Cs...>; }; 
template<std::uint32_t...Cs> 
using make_flags_t=typename make_flags<Cs...>::type; 

template<std::uint32_t value> 
class pValue_t 
{ 
    template<std::uint32_t StateMask, class flags> 
    friend class Compound;  
}; 

template<> class pValue_t<Flag0> 
{ 
public: 
    pValue_t() : 
     m_pValue0(reinterpret_cast<void*>(0xFFFFFFFF)) 
    {} 

protected: 
    void* m_pValue0; 
}; 

template<> class pValue_t<Flag1> 
{ 
public: 
    pValue_t() : 
     m_pValue1(reinterpret_cast<void*>(0xDEADBEEF)) 
    {} 

protected: 
    void* m_pValue1; 
}; 

template<> class pValue_t<Flag2> 
{ 
public: 
    pValue_t() : 
     m_pValue2(reinterpret_cast<void*>(0xCAFEBABE)) 
    {} 

protected: 
    void* m_pValue2; 
}; 

template<std::uint32_t StateMask, class flags> 
class Compound; 

template<std::uint32_t StateMask, std::uint32_t...Cs> 
class Compound< StateMask, flags_tag<Cs...> >: 
    public pValue_t<Cs>... 
{  
public: 
    void print() 
    { 
     if (IsStateValid(Flag0)) 
     { 
      std::cout << this->m_pValue0 << '\n'; 
     } 

     if ((StateMask & Flag1) == Flag1) 
     { 
      std::cout << this->m_pValue1 << '\n'; 
     } 

     // *** THIS IS THE PROBLEM STATEMENT *** 
     if (IsStateValid(Flag2)) 
     { 
      std::cout << this->m_pValue2 << '\n'; 
     } 

    } 

    static bool IsStateValid(std::uint32_t stateMask) 
     { return ((StateMask & stateMask) == stateMask); } 

    uint32_t m_stateMask; 
}; 

using my_type = Compound< Flag0 | Flag1, make_flags_t<Flag0, Flag1>>; 

int main() { 
    my_type test; 
    test.print(); 
} 

print函數包含參照m_pValue2,其是有效的,當StateMask包含Flag2

現在,編譯器警告它找不到m_pValue2。我希望編譯器在StateMask(編譯時已知)不包含Flag2(當IsStateValid()爲false)時刪除引用m_pValue2的代碼塊。

確切的錯誤如下:

main.cpp: In instantiation of 'void Compound<StateMask, flags_tag<Cs ...> >::print() [with unsigned int StateMask = 3u; unsigned int ...Cs = {1u, 2u}]': 
main.cpp:95:18: required from here 
main.cpp:80:27: error: 'class Compound<3u, flags_tag<1u, 2u> >' has no member named 'm_pValue2' 
      std::cout << this->m_pValue2 << '\n'; 

我希望這是可能的。在其他模板編程中,我用IsStateValid()來編譯出與StateMask不匹配的代碼段。然而,我從來沒有試過編譯掉一個可能丟失的成員變量。

有沒有人有任何想法?

回答

1

爲什麼不能在一個函數模板工作

所有分支機構將不分類型的編譯。在編譯時IsStateValid(Flag2)將是false,那if的正文必須是有效的代碼。由於在這種情況下沒有this->m_pValue2,這是一個嚴重的錯誤。

你能做些什麼來解決它

您需要轉發每個打印標記功能的函數模板,將打印該值(如果存在的話),或者什麼也不做(如果它不) 。我們可以使用函數重載來幫助這裏,並確保如果沒有這樣的標誌,整個函數將不會被實例化。例如:

void print() 
{ 
    printImpl<Flag0>(); 
    printImpl<Flag1>(); 
    printImpl<Flag2>(); 
} 

template <uint32_t F> 
void printImpl() { 
    printImpl<F>(std::is_base_of<pValue_t<F>, Compound>{}); 
} 

template <uint32_t F> 
void printImpl(std::true_type) { 
    // we DO have this flag 
    pValue_t<F>::print(); 
} 

template <uint32_t F> 
void printImpl(std::false_type) { 
    // we do NOT have this flag 
    // so do nothing 
} 

所有你需要在這一點上做的是添加適當的print()秒。例如:

template<> class pValue_t<Flag2> 
{ 
public: 
    pValue_t() : 
     m_pValue2(reinterpret_cast<void*>(0xCAFEBABE)) 
    {} 

    void print() { 
     std::cout << m_pValue2 << '\n'; 
    } 

protected: 
    void* m_pValue2; 
}; 
+0

你有一個工作的例子這個?我已經嘗試添加你的代碼到[我的實現](http://coliru.stacked-crooked.com/a/4ab856f0d23cbf7a),但它不會編譯。我試圖追查錯誤,但模板編程顯然不是我的專長 – pantaryl

+1

@pantaryl忘記模板參數,請參閱編輯。 – Barry

0

我得到這個工作(working example),但它似乎很hacky。它表明,這是可能的 - 希望有一個更有經驗的人比我能清理它。

這個想法是讓IsStataValid一個constexpr和有問題的代碼分離出來的另一個函數,它有兩個變種。在print()

static constexpr bool IsStateValid(std::uint32_t stateMask) 
    { return ((StateMask & stateMask) == stateMask); } 

template <typename A = void, 
      typename T = typename std::enable_if<IsStateValid(Flag2), A>::type> 
void blub(int x=0) { 
    std::cout << this->m_pValue2 << '\n'; 
} 

template <typename A = void, 
      typename T = typename std::enable_if<!IsStateValid(Flag2), A>::type> 
void blub(long x=0) { 
} 

然後代替if語句調用的輔助函數:那被實例化的類型取決於編譯時標誌

blub(); 

typename A是一個虛擬參數,使enable_if取決於模板參數,因此SFINAE可以踢入。blub s採用不同類型的第一個參數,因此編譯器不會抱怨它不能被重載。

+0

在我看來,這是C++的侷限性,你必須經過這些循環才能在編譯時獲得相當於「if()」的效果。人們可以很容易地編寫一個程序,通過編程轉換諸如'static_if(IsStateValid(Flag2)){x; } else {y; '進入上面。 – Claudiu

+1

就我個人而言,我只是更喜歡static_if ...如果它只存在於標準中。 – pantaryl

+1

@pantaryl:哦,看起來像這個建議[已存在](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3329.pdf)!不幸的是,Bjarne Stroustrup認爲它[「根本上有缺陷」](https://isocpp.org/files/papers/n3613.pdf),因此它可能不會輸入語言...... =( – Claudiu