假设我们有以下基类和派生类:
#include <string>
#include <iostream>
class Car {
public:
void Drive() { std::cout << "Baby, can I drive your car?" << std::endl; }
};
class Porsche : public Car {
};
..以及以下模板功能:
template <typename T, typename V>
void Function(void (T::*m1)(void), void (V::*m2)(void)) {
std::cout << (m1 == m2) << std::endl;
}
为什么使用GCC进行编译:
int main(int argc, char** argv) {
void (Porsche::*ptr)(void) = &Porsche::Drive;
Function(ptr, ptr);
return 0;
}
......但不是吗?
int main(int argc, char** argv) {
void (Porsche::*ptr)(void) = &Porsche::Drive;
Function(&Porsche::Drive, ptr);
return 0;
}
答案 0 :(得分:7)
int main(int argc, char** argv) {
void (Porsche::*ptr)(void) = &Porsche::Drive;
Function(&Porsche::Drive, ptr);
return 0;
}
ptr
的类型为void (Porsche::*)()
,但&Porsche::Drive
的类型为void (Car::*)()
(因为该成员位于Car
,而不是Porsche
) 。因此,调用的函数将这两个成员指针与这些类型进行比较,标准说明
此外,可以比较指向成员的指针,或指向成员的指针和空指针常量。执行成员转换(4.11)和资格转换(4.4)的指针以使它们成为通用类型。如果一个操作数是空指针常量,则公共类型是另一个操作数的类型。否则,公共类型是指向成员类型的指针,类似于(4.4)与其中一个操作数的类型,具有cv-限定签名(4.4),它是操作数类型的cv限定签名的并集。
4.11
描述了从void (Base::*)()
到void (Derived::*)()
的隐式标准转换。因此,比较将找到共同类型void (Porsche::*)()
。对于类型为Porsche
的对象,两个成员指针都将引用相同的函数(即Car::Drive
) - 因此比较将产生真。 comeau web compiler遵循此解释并编译您的代码。
答案 1 :(得分:1)
使用g ++ 4.0.1,您提供的示例编译正常,但在处理不同类型的成员函数指针的比较时存在问题(即使比较的元素是虚方法。
struct base
{
void f() {}
virtual void g() {}
};
struct derived : public base
{
void f() {}
virtual void g() {}
};
template <typename T, typename U>
bool cmp( void (T::*lhs)(), void (U::*rhs)() ) {
return lhs == rhs;
}
int main()
{
void (base::*bp)() = &base::f;
void (base::*bvp)() = &base::g;
cmp( bp, &base::f ); // compiles
cmp( bvp, &base::g ); // compiles
void (derived::*dp)() = &derived::f;
void (derived::*dvp)() = &derived::g;
cmp( dp, &derived::f ); // compiles
cmp( dvp, &derived::g ); // compiles
cmp( bp, dp ); // fails to compile
cmp( bvp, dvp ); // fails to compile
}
现在,我已经使用comeau在线编译器进行了相同的测试,整个代码编译得很好。我不能马上告诉你什么是标准行为。我将不得不浏览一段时间的标准。
编辑:我猜不是,litb已经做了。