2017-12-27 366 views
2

我想專精getVector成員函數,我正在嘗試使用SFINAE。但它只有在Dim爲3或更大時纔有效。SFINAE模板成員超載

template <size_t Dim> 
class Mat 
{ 
    ... 
    template <size_t VDim, typename enable_if<(Dim > 1 && VDim == 0)>::type* = nullptr> 
     void getVectorBegin(const array<size_t, Dim - 1>& indexAfter) const; 

    template <size_t VDim, typename enable_if<(Dim > 2 && 0 < VDim && VDim < Dim-1)>::type* = nullptr> 
     void getVectorBegin(const array<size_t, VDim>& indexBefore, const array<size_t, Dim - VDim - 1>& indexAfter) const; 

    template <size_t VDim, typename enable_if<(Dim > 1 && VDim == Dim-1)>::type* = nullptr> 
     void getVectorBegin(const array<size_t, Dim - 1>& indexBefore) const; 
}; 

Mat<3> m; 
Mat<2> m; // error C2039: 'type': is not a member of 'std::enable_if<false,_Ty>' 
+0

您是否嘗試將「Dim> x」檢查作爲最後的&&術語?我不確定編譯器是否正確,或者它只是實現定義的,但它可能會縮短enable_if條件,並認爲它的值不依賴於VDim,因此在成員聲明實例化期間發生編譯器錯誤(即,在Mat <2>瞬間點) –

+0

有些東西你沒有向我們展示。如果不使用模板類的成員函數,則不會實例化。你能告訴我們呼叫網站嗎? – papagaga

+4

@papagaga這不完全正確,成員函數聲明(但不是它們的定義)在類模板時被實例化。因此,如果聲明在類模板參數中包含一個僅依賴於值的表達式,並且表達式結果不正確,那麼即使該函數未被「使用」也會產生錯誤... –

回答

2

這將是棘手。

我相信你的代碼對於Dim>=2是可以的,但是當給出Dim<=1參數時不合格,不需要診斷,但是由於編譯器抱怨的原因不同。


對於Dim>=2它是正確的,但你的編譯器(MSVC++我猜)在Dim==2情況下抱怨。鑑於錯誤描述,我想這是因爲Dim > x爲false時,它錯誤地短路了enable_if條件中的表達式,並將它們解釋爲值類型僅在類模板參數上。解決方法是將Dim > x檢查作爲& &表達式的最後一項移動。

爲了詳細說明,這種情況在概念上類似於下面的代碼片段:

template <size_t N> 
class foo 
{ 
    template <typename E = enable_if_t<(N>0)>> 
    void bar(); 
}; 

foo<1> f1; 
foo<0> f0; // fails 

這裏的foo的instantation觸發聲明的成員的實例化(而不是定義)(見[ temp.inst]#1);但是隻有在成員上依賴的名稱和表達式的檢查纔會在它們各自的實例化點被推遲。這裏,類型名enable_if_t<(N>0)>確實依賴於任何bar()模板參數(N屬於foo的模板參數列表),因此是不相關的,從而導致enable_if<false>::typeN == 0,所以該錯誤。


再回到您的代碼,可以考慮:

[temp.dep.constexpr]#1除了下面所描述的,一個常量表達式是值依賴性如果任何子表達式是取決於數值

並沒有提到任何短路運營商作爲例外。因此,表達式即Dim > 1 && VDim == 0是值依賴的,即使Dim<=1;因此,在替代VDim(其中SFINAE將適用)之前不應出現錯誤。實際上,gcc和叮噹agree接受你的代碼。

這就是說,Dim<=1第一和第三getVectorBegin重載實際聲明功能上等同成員模板時(見[temp.over.link]#6),所以我認爲這是非法的構造在這種情況下。