2016-09-15 155 views
5

我一直在試圖理解C++選擇模板的方式。也就是說,考慮下面的代碼示例:會員功能模板選擇和SFINAE

template <typename R> 
class Curious 
{ 
public: 
    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type = 33> 
    void test1() {} 

    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type = 33> 
    void test1() {} 

    template <typename T, typename = typename std::enable_if<std::is_const<T>::value>::type> 
    void test2() {} 

    template <typename T, typename = typename std::enable_if<!std::is_const<T>::value>::type> 
    void test2() {} 

    template <typename std::enable_if<std::is_const<R>::value>::type * = nullptr> 
    void test3() {} 

    template <typename std::enable_if<!std::is_const<R>::value>::type * = nullptr> 
    void test3() {} 

    // works 
    template <typename T = void> 
    typename std::enable_if<std::is_const<R>::value, T>::type test4() {} 

    template <typename T = void> 
    typename std::enable_if<!std::is_const<R>::value, T>::type test4() {} 

    // also works 
    template <typename T = void, typename std::enable_if<std::is_const<R>::value, T>::type * = nullptr> 
    void test5() {} 

    template <typename T = void, typename std::enable_if<!std::is_const<R>::value, T>::type * = nullptr> 
    void test5() {} 
}; // Curious 

前兩個功能(測試1)正常工作(爲什麼):

Curious<int> curious; 
curious.test1<int>(); 
curious.test1<const int>(); 

雖然他們的休息導致編譯錯誤。 關於test2的編譯器要求我試圖創建一個重複的功能:

error C2535: 'void Curious::test2(void)': member function already defined or declared

Here的文件說:

一個常見的錯誤是聲明中的區別僅 二元函數模板,其默認模板參數。這是非法的,因爲默認 模板參數不是函數模板簽名的一部分,並且 聲明具有相同簽名的兩個不同函數模板是 非法。

所以它似乎是這種情況。但是,我沒有看到與前兩個函數有很大區別,它們也有默認的模板參數。因此,我們有一個默認類型(test2 - 不起作用)與默認值(test1 - works)。有關於它的規則嗎?

在test3的情況下:

error C2039: 'type': is not a member of 'std::enable_if'
與第一種情況一樣,這次成員函數模板有一個默認的非類型參數,但它取決於類模板參數。現在SFINAE不會跳過錯誤的(也不知道爲什麼)。

在第四種情況下,SFINAE通過返回類型解析模板。但是這些test4函數不具有相同的簽名嗎?因爲它們僅在返回類型上有所不同。

據我所知,在第五種情況下,添加額外的參數使得test5簽名依賴於函數模板參數,因此SFINAE踢入和解析工作。

我很困惑C++如何處理這些模板。有人可以善待這些事嗎?

+1

作爲一個說明,因爲C++ 14,你可以替換'類型名稱的std :: enable_if :: type'用'的std :: enable_if_t '。 – Jarod42

回答

6
  • 在默認值移除,爲test1,您有:

    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type> 
    void test1(); 
    
    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type> 
    void test1(); 
    

    其中有明顯不同的特徵。

  • 對於測試2:

    template <typename T, typename> void test2(); 
    
    template <typename T, typename> void test2(); 
    

    這些顯然是相同的簽名。

  • 對於TEST3,SFINAE並不像你一樣R固定在班級和你enable_if不依賴於功能的模板參數具有硬錯誤適用。

  • 對於TEST4,有關於模板函數簽名的例外,因爲超載會返回類型只相差這麼

    int foo(); 
    char foo(); // Illegal. 
    

    template <typename T> int foo(); 
    template <typename T> char foo(); // legal, even it is not trivial to call 
    

    此外,std::enable_if<!std::is_const<R>::value, T>::type取決於模板參數T所以沒關係。

  • 對於test5,第二個模板參數取決於第一個模板參數T,所以也可以。

+0

謝謝,我剛剛在原始文章中添加了兩個例子 – mentalmushroom

+0

添加了test4/test5的解釋:'enable_if'在兩種情況下都取決於'T'。 – Jarod42