为什么模板参数推导失败指针到成员函数?

fiz*_*zer 13 c++ templates argument-deduction

用g ++ 5.4,这个

struct B {
    void f() {}
}; 

struct D : public B {
    void g() {}
};

template <class T>
void foo(void (T::*)(), void (T::*)())
{}

int main()
{
    foo(&D::f, &D::g);
}
Run Code Online (Sandbox Code Playgroud)

由于"推断出参数'T'('B'和'D')的冲突类型而失败".为什么不将T推导为D,完全匹配?

use*_*670 13

类型&D::f将是void ( B::* )(void)

static_assert(::std::is_same<void ( B::* )(void), decltype(&D::f)>::value, "");
static_assert(::std::is_same<void ( D::* )(void), decltype(&D::f)>::value, ""); // error
static_assert(::std::is_same<void ( D::* )(void), decltype(&D::g)>::value, "");
Run Code Online (Sandbox Code Playgroud)

这背后的基本原理是,否则即使是成员或比较,您也无法在没有强制转换的情况&D::f下为变量赋值.void ( B::* )(void)fB&D::f == &B::f

作为解决方法,您可以执行static_cast:

foo(static_cast<void (D::*)(void)>(&D::f), &D::g);
Run Code Online (Sandbox Code Playgroud)

  • 一个更简单的解决方法是`foo <D>(&D :: f,&D :: g)` (4认同)

Sto*_*ica 12

除了VTT的出色演示.我相信,有问题的标准文本是[expr.unary.op]/3,强调我的:

一元&运算符的结果是指向其操作数的指针.操作数应为左值或限定ID.如果操作数是一个qualified-id,命名一个类型为T的某个类C的非静态或变体成员m,则结果类型为"指向类型为C的C类成员的指针",并且是一个指定C ::的prvalue米.

您使用的限定ID是D::f,但它命名成员函数B(如果需要,我可以调出查找规则).所以上面的类类型CB.因此该类型解析为void ( B::* )(void).

  • 我最初不相信,但我从[class.mem]看到 - "类定义中的成员规范声明了类的完整成员集;没有成员可以添加到其他地方." - 尽管有D :: nested-name-specifier,但f明确地是B的成员而不是D的成员. (4认同)