Mik*_*ike 4 c++ member-pointers
我有关于成员指针的问题.以下代码无法使用Oracle Solaris Studio 12.2的CC和cygwin GCC 4.3.4进行编译,但与Microsoft Visual C++ 2010一起使用:
struct A {
int x;
};
struct B : public A {
};
template<typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
};
int main(int, char *[]) {
Bar<B> bbar;
bbar.foo(&B::x);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在倒数第二行,上面提到的两个编译器都找不到匹配项Bar<B>::foo(int A::*).我写了一个简单的测试来确认表达式的类型&B::x实际上是int A::*:
// ...
static void foo(int A::*p) {
std::cout << "A" << std::endl;
}
static void foo(int B::*p) {
std::cout << "B" << std::endl;
}
int main(int, char *[]) {
foo(&B::x); // prints "A", even on MS VC++ 2010
return 0;
}
Run Code Online (Sandbox Code Playgroud)
以下解决方法适用于GCC(尚未使用Oracle CC进行测试)但由于模糊性而无法使用VC++:
template<typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
foo(static_cast<M T::*>(p));
}
};
Run Code Online (Sandbox Code Playgroud)
我的问题:哪种行为是正确的?显然,VC++确实从隐含上溯造型int A::*,以int B::*满足调用成员函数模板,不应该另外两个编译考虑效仿呢?
从A转换int A::*到int B::*被允许的,这不是问题.问题在于模板参数推导,因为你可以看到你是否尝试过以下程序,该程序<int>为B::foo和编译提供模板参数,以及一个非成员函数foo2,它产生与B::foo以前相同的错误.
struct A {
int x;
};
struct B : public A {
};
template <typename T> class Bar {
public:
template<typename M> void foo(M T::*p);
};
template<typename M> void foo2(M B::*p);
int main(int, char*[]) {
Bar<B> bbar;
bbar.foo<int>(&B::x);
foo2(&B::x); // error, but foo2<int>(&B::x) would work.
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我认为编译器应该自己推断模板参数的情况不包括这种情况<int>.14.8.2.1p3:
通常,推导过程试图找到模板参数值,这将使推导出的A与A相同(在如上所述转换类型A之后).但是,有三种情况可以产生差异:
这里"P"是模板函数的参数类型:M B::*p,其中M要确定模板类型参数."A"是实际参数的类型:int A::*.P和A是肯定不是一个参考或一个类,以及排序指针到构件转换我们需要对于这个工作是不是一个资格转换(其描述了像只有常量/易失性的操作X*来const X*或int X::*到const int X::*).
因此无法推导出模板参数,您应该将<int>显式模板参数添加到代码中.