2012-12-31 33 views
6

該代碼失敗在大多數編譯器來編譯,但起初我直覺預期SFINAE保護我:SFINAE,演繹與實例

typedef void (*A)(); 

template < typename T > 
struct a_metafun { typedef typename T::type type; }; 

template < typename T > 
typename a_metafun<T>::type f(T) {} 

template < typename T> 
void f(T(*)()) {} 

int main() { f(A()); } 

我可以以至少兩種方式解決這個問題1)改變的「metafun」 F(定義),以:

template < typename T > typename T::type f(T) {}

2)界定「a_metafun」,使得它分析T和具有類型,如果T具有一個,並且不我f,將它不...但沒有實例化錯誤兩種方式:

BOOST_MPL_HAS_XXX_TRAIT_DEF(type) 

typedef < template T, bool = has_type<T>::value > 
struct a_metafun { }; 

typedef < template T > 
struct a_metafun<T, true> { typedef typename T::type type }; 

在觀看14.8.2(C++ 03),它看起來對我來說,它僅指定了下情況SFINAE可以申請什麼。有更好的地方看嗎?在已經推導出的模板的實例化中失敗,即使在扣除另一個模板的過程中,似乎也不包括在這個列表中。

我已經採取了另一個解釋是什麼使得這個非法的方向是a_metafun的演繹已經發生並且其內部的實例化是導致錯誤的原因。 SFINAE在實例化過程中不適用,但僅在扣除過程中使用,或者我錯了嗎?但在第二種情況下,a_metafun正確,並且實例化良好,但它內部沒有「類型」定義,這意味着嘗試實例化它的模板由於替換而失敗。

基本上我想知道標準中的什麼指定了我目睹的行爲。我試過的每一個編譯器都會抱怨,甚至是冒險島。我認爲他們這樣做是正確的,我只是不完全確定爲什麼。

那麼,專家......那是什麼?爲什麼類型的實例化,即使在f()中的演繹上下文中導致錯誤而不是SFINAE排除?

+0

我認爲,應該在C++ 11失敗,而不是在C++ 03雖然。 SFINAE規則(或者*措辭*)在C++ 11中略有改變。 – Nawaz

回答

2

SFINAE不會保護你,發生錯誤型扣除。然而,這應該工作:

template < typename T, typename Type = typename T::type > 
struct a_metafun { typedef Type type; }; 

通過默認的模板參數我們造成這在置換時有發生accesing T::type,而當時SFINAE

編輯:思前想後還有一些,我不確定你當前的實現失敗的原因。我認爲是因爲a_metafun成員類型type,導致編譯錯誤;如果a_metafun根本沒有成員類型type,則會有所不同。

+0

看到我的答案爲什麼它在C++ 11中失敗。 – Nawaz

4

在C++ 03規範中,SFINAE的規則有點含糊,允許編譯器作者去的任何長度去查找替換失敗導致SFINAE。相關的文字§14.8.2/ 2從C++ 03說,

- [...]如果在模板參數或函數模板結果的功能類型中的類型無效的取代,類型扣除失敗[...]

它進一步解釋了失敗的幾個原因,但他們中沒有一個真正說過替代失敗應該被視爲SFINAE。所以我想,你的代碼在C++ 03中可能工作的很好(或者根據編譯器作者是如何解釋文本的,這可能會讓我感到困惑)。

但是,C++ 11中的措辭已經改進,消除了模糊性。它在§14.8.2/ 8中提到,

如果替換導致無效類型或表達式,則鍵入演繹失敗。無效的類型或表達式是使用替代參數編寫的格式不正確的類型或表達式。 [注意:訪問檢查是作爲替換過程的一部分完成的。 - 注意]只有函數類型及其模板參數類型的上下文中的無效類型和表達式纔會導致扣除失敗。

術語「直接上下文」很有意思,我認爲它適用於你的情況。更具體地說,元函數a_metafun中的替換失敗不被視爲函數類型的「直接上下文」。它在C++ 11中形成,而不是SFINAE。

「直接上下文」的定義在C++ 11中還不夠清楚。這是一個積極的問題:

+1

的確很有意思!我沒有意識到_SFINAE_中的這一變化。謝謝 –