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