考虑以下代码:
#include <iostream>
#include <functional>
struct B {
template <class C, class M, class T>
void call1(C (M::*member)(), T *instance) {
std::function<void()> fp = std::bind(member, instance);
fp();
}
template <class C, class M, class T>
void call2(C (M::*member), T *instance) {
std::function<void()> fp = std::bind(member, instance);
fp();
}
void foo() {
call1(&B::func, this); // works
call2(&B::func, this); // works
call1(&B::func2, this); // Error: no matching member function for call to 'call2'
call2(&B::func2, this); // works
}
void func() {
std::cout << "func\n";
}
void func2() const volatile {
std::cout << "func2\n";
}
};
int main() {
B{}.foo();
}
似乎更高的版本不接受带有额外的cv限定词的功能。
这样指定函数成员指针C (M::*member)()
和这样指定函数成员指针有什么区别C (M::*member)
?
让我们简化为仅考虑以下两者之间的区别:
template <class C, class M> void f(C (M::*member)());
template <class C, class M> void g(C (M::*member));
在中f
,member
是指向M
返回零值并返回的成员函数的指针C
。如果使用调用&B::func
,编译器将推导M == B
和C == void
。直截了当。
在中g
,member
只是一个指向M
type成员的指针C
。但是,就我们而言,&B::func
是一个函数。因此,这里的影响只是放下了指针。我们M == B
再次推断出,而C
变为void()
-现在C
是一个函数类型。这是不那么专业的版本,f
因为它允许更多类型的成员。g
可以与带有参数的函数,与成员的指针匹配,或者与cv限定的成员functinos匹配。
让我们以一个重载函数为例,以及如何对其进行不同的推导(这是您问题中的原始问题,此问题已被编辑,但仍然很有趣):
struct X {
void bar() { }
void bar(int ) { }
};
当我们这样做时:
f(&X::bar);
即使&X::bar
是一个重载的名称,实际上也只有一个匹配C (M::*)()
。具有M == X
和的那个C == void
。我们根本没有办法过载bar
服用int
模板类型相匹配。因此,这是传递重载名称的可接受用途之一。这会导致罚款。
但是,当我们这样做时:
g(&X::bar);
现在,有两个完全有效的推论。C
可能是void()
和void(int)
。由于两者都是有效的,因此推论是模棱两可的,并且您无法编译-并没有真正使这一点特别清楚的错误。
现在回到您的示例:
call1(&B::func2, this); // Error: no matching member function for call to 'call2'
call2(&B::func2, this); // works
的类型&B::func2
是void (B::*)() const volatile
。由于call1
推导不带参数且不具备cv资格的成员函数类型,因此类型推导只会失败。没有C
或没有M
要匹配的类型。
但是,这种call2
推论很好,因为它更通用。它可以匹配任何指向成员的指针。我们最终以结束C = void() const volatile
。这就是为什么这样。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句