2017-03-25 106 views
0
#include <iostream> 
#include <type_traits> 

template<typename T> 
struct A 
{ 
    using m = std::remove_pointer_t<T>&; 
}; 

template 
< 
    typename T, 
    typename = std::void_t<> 
> 
struct Test 
{ 
    enum { value = 0 }; 
}; 

template<typename T> 
struct Test<T, typename A<T>::m> 
{ 
    enum { value = 1 }; 
}; 

int main() 
{ 
    std::cout << Test<void*&>::value; // ok, output 0 
    std::cout << Test<void*>::value; // error : cannot form a reference to 'void' 
} 

第一種情況輸出0,這意味着主模板被選中。所以,我認爲第二種情況也應該選擇主模板而不是專用模板;那麼,不應該有錯誤。爲什麼SFINAE在這種情況下不起作用?

預計Test<void*&>沒問題;令我驚訝的是Test<void*>應該不行!

爲什麼SFINAE不適用於後一種情況?

+0

可能是這種情況,因爲'A :: m'是一個不可推論的上下文。 – Pixelchemist

+0

如果是這樣,爲什麼前一種情況好嗎? – xmllmx

+0

由於參考摺疊和'remove_pointer'沒有效果。 – Pixelchemist

回答

5

你的第二種情況是一個硬性錯誤。

SFINAE @ cppreference.com說:

只有在類型和表達式的函數類型或它的模板參數類型的直接背景失敗是SFINAE錯誤。如果替代類型/表達式的評估會導致一些副作用,例如某些模板特化的實例化,隱式定義的成員函數的生成等,則這些副作用中的錯誤將被視爲硬錯誤。

第一種情況是可以的,因爲remove_pointer沒有效果,如果是Tvoid*&,然後mvoid*&因爲參考塌陷和指針的引用中的孔隙是一個有效的類型,而到一個無效引用不是的。

第一種情況下的類型仍然是Test<void*&, void>而不是Test<void*&, void*&>,因爲您只指定第一個模板參數。

第二種情況失敗但不是第一種情況的原因是編譯器必須實例化專用模板,因爲第二個參數是非推導的上下文,因此編譯器無法立即告知專業化是否會更好地匹配。但在第二種情況下,實例化會產生一個硬錯誤,而在第一種情況下則不會。

主模板仍然被選中,因爲它是一個更好的匹配(雖然專門的模板將被實例化以檢查它是否匹配)。

注意:我不能說專業化是否實際完全實例化,或者編譯器是否正在查找typename A<T>::m以檢查這種專業化是否會更好地匹配。但結果是一樣的。在void*的情況下有一個硬錯誤。

還要注意,當使用C++ 17時,可能寧願使用一個constexpr成員而不是枚舉。

template<typename T> 
struct Test<T, typename A<T>::m> 
{ 
    static constexpr unsigned value = 1u; 
}; 
+0

第一種情況輸出'0',這意味着重載類是主模板而不是專用類。 – xmllmx

+0

如果您的陳述是真實的,那麼第一個案例將輸出'1',因爲它使用專門的模板。 – xmllmx

+0

如果第一種情況輸出「0」,則第二種情況也應輸出「0」。這正是我想知道的。 – xmllmx

相關問題