4

我正在尋找刪除一些未使用的重載,並觸發編譯錯誤,編譯器說這是一個模板替換錯誤。但我認爲「替代失敗並不是一個錯誤」,無論如何,爲什麼要消除超載導致它?刪除未使用的重載會導致編譯錯誤?

簡單的開始:

#include <string> 

int ParseInt(const char *); 
int ParseInt(std::string); 

bool F(int(*)(const char *)); 

bool User() { 
    return F(ParseInt); 
} 

在這裏,用戶()是調用f控制解析程序的地址。一切都很好。 ParseInt被重載,但只有一個重載符合F的簽名。

輸入模板超載的F:

bool F(int(*)(const char *)); 

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

template <typename T> 
typename MetaDeduce<T>::type F(const T&); 

現在有公司的F的這種怪異的模板超載,但它是很好,因爲函數指針沒有名爲X反正成員。一切順利,一切都很好。

UNTIL ....

#include <string> 

int ParseInt(const char *); 
// int ParseInt(std::string); // commenting this out caused a compiler error! 

bool F(int(*)(const char *)); 

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

template <typename T> 
typename MetaDeduce<T>::type F(const T&); 

bool User() { 
    return F(ParseInt); 
} 

如上godbolt(http://goo.gl/2Yd04p)中可以看出,這將產生一個奇怪的編譯錯誤:

10 : error: type 'int (const char *)' cannot be used prior to '::' 
    because it has no members 
typedef typename T::X type; 
^ 
14 : note: in instantiation of template class 'MetaDeduce<int (const char *)>' 
    requested here 
typename MetaDeduce<T>::type F(const T&); 
^ 

WTF ???它看起來像編譯器抱怨替代失敗,但爲什麼之前沒有這個問題?無論如何,我認爲替代失敗並不是一個錯誤!這是怎麼回事?

回答

4

這是一個微妙的互動二語性質

  1. SFINAE只適用於即時情境之間產生。您的MetaDeduce<T>沒有在這種直接上下文中定義,這使得typename MetaDeduce<T>::type是一個嚴重錯誤。但是SFINAE確實適用於typename T::X,即使ParseInt(std::string)超載被註釋掉,也可以使用它來編譯代碼。

    template <typename T> 
    typename T::X F(const T&); 
    

Live Example(請注意,您收到鏈接錯誤,因爲你還沒有定義的函數)

  • 所以如果typename MetaDeduct<T>::type是罪魁禍首,爲什麼它是否與兩個過載ParseInt一起工作?那麼,考慮一下如果你只有模板FParseInt s會發生什麼情況。爲了方便起見,給這個模板F布爾返回類型忽略替換失敗的那一刻。

    int ParseInt(const char *); 
    int ParseInt(std::string); 
    
    template <typename T> 
    bool F(T const&); 
    
    bool User() { return F(ParseInt); } // error, cannot deduce template argument 
    
  • Live Example

    隨着ParseInt兩個重載和呼叫F(ParseInt)沒有額外的信息,編譯器無法推斷出的ParseInt版本應該匹配模板參數T。在這段代碼中,這將導致一個硬錯誤(因爲您將有一個空的重載集),但是由於額外的非模板過載F(int(*)(const char*))它不會(因爲ParseInt(const char*)將匹配)。請注意,因爲參數扣除在這裏失敗,在返回類型爲typename MetaDeduce<T>::Type的情況下,參數替換甚至不會發生。

    實際上,ParseInt的兩個重載屏蔽了您在非直接上下文中的置換失敗。一旦你拿走它們中的一個,論證扣除就會成功,非即時替代失敗會導致嚴重錯誤。

    這有點像穿過一盞紅燈的街道,因爲兩輛迎面而來的卡車在撞到你之前相互碰撞而得救。只有一輛卡車,你會受到打擊。

    +0

    返回類型中'typename MetaDeduct :: type'是直接上下文中的 - 如果'MetaDeduct'沒有名爲'type'的成員類型,那麼您將獲得SFINAE。然而,'MetaDeduct'的定義不是直接的上下文,並且在那裏發生錯誤,所以這是一個嚴重的錯誤。 –

    +0

    @ T.C。謝謝,並更新。 – TemplateRex

    相關問題