我正在考虑删除一些未使用的重载,并触发编译错误,编译器称这是一个模板替换错误。但我认为“替换失败不是错误”,无论如何,为什么要删除过载导致它?
简单的开头:
#include <string>
int ParseInt(const char *);
int ParseInt(std::string);
bool F(int(*)(const char *));
bool User() {
return F(ParseInt);
}
这里,User()使用解析例程的地址调用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 ???看起来编译器抱怨替换失败,但为什么以前不是问题呢?无论如何,我认为替换失败不是错误!发生了什么事?
答案 0 :(得分:4)
这是由两种语言属性之间的微妙交互引起的
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
的两个重载一起使用?好吧,如果你只有模板F
和ParseInt
,那么请考虑一下会发生什么。为方便起见,将模板F
赋予bool返回类型,以便在替换失败时忽略。
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
如果ParseInt
有两个重载且调用F(ParseInt)
中没有额外信息,则编译器无法推断出ParseInt
的哪个版本应与模板参数T
匹配。在此代码段的上下文中,这将导致硬错误(因为您将有一个空的重载集),但是由于额外的非模板重载F(int(*)(const char*))
它不会(因为ParseInt(const char*)
会匹配那个)。请注意,由于参数推导在这里失败,如果返回类型为typename MetaDeduce<T>::Type
,则此处甚至不会发生参数替换。
实际上,ParseInt
的两个重载使您免于非直接上下文中的替换失败。一旦你拿走其中一个,参数推断成功,非立即替换失败导致一个硬错误。
这有点像穿过红灯的街道,并且因为两辆迎面而来的卡车在撞到你之前相互碰撞而得救了。只有一辆卡车,你就会受到打击。