billz 已经给出了答案,但我会尽力做出解释。
在 C++ 中,当获取指向成员的指针时,表达式的结果不是指向表达式中存在的类型的成员的指针,而是指向定义该成员的类型的成员的指针。也就是说,表达式&B::Key
产生&A::Key
,因为成员函数Key
是在A
而不是在 中定义的B
。这是在 §5.3.1/3 中定义的,很难阅读,但附带了一个示例:
一元 & 运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定id,命名某个类C的一个非静态成员m,类型为T,则结果的类型为“指向类型T的类C成员的指针”,并且是一个指定C::m的纯右值。否则,如果表达式的类型是 T,则结果的类型为“指向 T 的指针”,并且是一个纯右值,它是指定对象 (1.7) 的地址或指向指定函数的指针。[注意:特别是,类型“cv T”的对象的地址是“指向cv T的指针”,具有相同的cv-qualification。— 尾注] [ 示例:
struct A { int i; };
struct B : A { };
... &B::i ... // has type int A::*
—结束示例]
这意味着您的模板实例化等效于:
TT<B, &A::Key>
虽然指向基成员的成员指针可以转换为指向派生类型的成员指针,但对于非类型非模板模板参数的特殊情况,这种转换是不允许的。非类型非模板模板参数的转换在 §14.3.2/5 中定义,对于这种特殊情况说明:
对于指向成员函数的类型指针的非类型模板参数,如果模板参数的类型为 std::nullptr_t,则应用空成员指针转换 (4.11);否则,不适用任何转换。
由于&A::Key
不能收敛到int (B::*)() const
,因此模板实例化格式不正确。通过在模板实例化中添加强制转换,您将强制转换在实例化模板之前发生,并且实例化变得有效,因为不需要转换。