标题引自此SO answer。它正在讨论使用SFINAE来检测具有给定签名的成员函数的存在,并指出在处理继承的成员函数时接受的答案中的方法失败。特别是,给出的解释如下
如果你还不熟悉这个问题,那么在标题中查看
std::shared_ptr<T>
的定义将会有所启发。在该实现中,std::shared_ptr<T>
派生自继承operator*() const
的基类。因此模板实例化SFINAE<U, &U::operator*>
构成&#34;发现&#34;U = std::shared_ptr<T>
的运算符不会发生,因为std::shared_ptr<T>
本身没有operator*()
,模板实例化不会继承&#34;。这个障碍不会影响众所周知的SFINAE方法,使用&#34; sizeof()Trick&#34;,仅用于检测T是否具有某个成员函数mf(参见例如此答案和评论)。
使用答案中的术语,使用T::mf
作为模板参数来实例化类型与编译器通过模板函数参数推断确定它有什么区别?什么&#34;模板实例化不做继承&#34;意思?最后,为什么这不会影响检查成员的存在,例如here?
答案 0 :(得分:3)
最小化的例子:
struct A {
void a() const;
};
struct B : A {};
template<typename U, void (U::*)() const> struct SFINAE {};
template<typename U> void Test(SFINAE<U, &U::a>*) { }
int main(void)
{
Test<B>(0); // doesn't compile
return 0;
}
问题是当B::a
从A
继承时,&B::a
的类型实际上是“指向A
成员的指针” - 而且通常是指针-to-member-of-base可以隐式转换为指向派生成员的指针,根据§14.3.2[temp.arg.nontype] / p5,此转换不适用于非类型模板参数:
对用作a的每个表达式执行以下转换 非类型模板参数。如果非类型模板参数不能 转换为相应的模板参数的类型 然后该计划形成不良。
- [...]
- 对于指向成员函数的类型指针的非类型模板参数, 如果template-argument的类型为
std::nullptr_t
,则为 应用null成员指针转换(4.11);否则,没有 转换适用。如果 template-argument 表示一组 重载成员函数,选择匹配成员函数 从集(13.4)。