我有关于成员指针的问题。以下代码无法使用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;
}
在倒数第二行,上面提到的两个编译器都找不到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;
}
以下解决方法适用于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));
}
};
我的问题:
哪种行为是正确的?显然VC ++从int A::*
到int B::*
执行隐式向上转换以满足对成员函数模板的调用,其他两个编译器是否应该考虑做同样的事情呢?
答案 0 :(得分:7)
允许从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;
}
我认为编译器应该自己推导出模板参数<int>
的情况不包括这种情况。 14.8.2.1p3:
通常,推导过程会尝试查找模板参数值,这些参数值将使推导出的A与A相同(在如上所述转换类型A之后)。但是,有三种情况可以产生差异:
此处“P”是模板函数的参数类型:M B::*p
,其中要确定模板类型参数M
。 “A”是实际参数的类型:int A::*
。 P和A当然不是引用或类,并且我们需要这种指针到成员的转换类型不是限定转换(它仅描述X*
到{const X*
之类的常量/易失性操作{1}}或int X::*
至const int X::*
)。
因此无法推断出模板参数,您应该将<int>
显式模板参数添加到代码中。