VS成员指针的实现是不是坑爹了?

iamnobody 2014-08-28 05:19:54
不知道其他编译器会不会这样.
代码:


#include <iostream>
#include <string>
class A
{
public:
void afun()
{
std::cout << am;
}
std::string am;
};

class B
{
public:
void bfun()
{
std::cout << bm;
}
std::string bm;
};

class C:public A,public B
{
public :
void cfun()
{
std::cout << cm;
}
std::string cm;
};


int main()
{
C ci;
ci.am = "aaaaa";
ci.bm = "bbbbb";
ci.cm = "ccccc";
B * pb = &ci;

// case 1: 5.2.9.9 ISO/IEC 14882:2003(E)
void (A::*pam)() = &A::afun;
void (C::*pcm)() = &C::cfun;
void (B::*pbm)() = static_cast<void (B::*)()>(pcm); // VS2012 says: warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码
(pb->*pbm)();

// case 2: 5.2.10.9 ISO/IEC 14882:2003(E)
pcm = reinterpret_cast<void (B::*)()>(pcm); // VS2012 says :error C2440: “reinterpret_cast”: 无法从“void (__thiscall C::* )(void)”转换为“void (__thiscall B::* )(void)”
// 你妹,static_cast 都能过的,reinterpret_cast就变错误了.
system("pause");
return 0;
};


注:

5.2.9.9 ISO/IEC 14882:2003(E)
5.2.9 Static cast
9.An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to
member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from
“pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same
cv-qualification as, or greater cv-qualification than, cv1.
63)The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or
is a base or derived class of the class containing the original member, the resulting pointer to member
points to the original member. Otherwise, the result of the cast is undefined.[Note: although class B need
not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ]


5.2.10.9 ISO/IEC 14882:2003(E)
5.2.10 Reinterpret cast
9 An rvalue of type “pointer to member of X of type T1” can be explicitly converted to an rvalue of type
“pointer to member of Y of type T2” if T1 and T2 are both function types or both object types.66) The null
member pointer value (4.11) is converted to the null member pointer value of the destination type. The
result of this conversion is unspecified, except in the following cases:
— converting an rvalue of type “pointer to member function” to a different pointer to member function
type and back to its original type yields the original pointer to member value.
— converting an rvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back
to its original type yields the original pointer to member value.
...全文
458 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
iamnobody 2014-08-29
  • 打赏
  • 举报
回复
引用 12 楼 jwj070524 的回复:
接楼上。上面的代码是用g++跑的,我在vs2010上测试得到不一样的结果,这次的输出全是错误的值,-858993460的十六进制形式是0xCCCCCCCC,正好是vs2010 Debug版本中的未初始化内存区域的内容。所以这个特性不同的编译器有不同的实现啊。

sizeof(C) = 12
sizeof(B) = 4
p1: 0037FCE8
C this 0037FCE8, data -858993460
p2: 0037FCCC
C this 0037FCCC, data -858993460
请按任意键继续. . .
嗯,G++ 的实现是对的, 的确应该调整this指针的位置, VS的实现是错的,太坑了.
winnuke 2014-08-29
  • 打赏
  • 举报
回复
难道不是输出ccccccc?
iamnobody 2014-08-29
  • 打赏
  • 举报
回复
引用 19 楼 mingliang1212 的回复:
[quote=引用 16 楼 unituniverse2 的回复:] 5.2.9.9 ISO/IEC 14882:2003(E) 5.2.9 Static cast 9.An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. 63)The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined.[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ] 虽然你觉得在这里gcc能给个确定的结果而vc却不能,但是UB就是UB,不存在谁对谁错的问题了。而你注意本人测试代码里连cast都没有用到,直接编译就通过了,gcc和vc结果也一致
你看错了,不是ub, or is a base or derived class of the class containing the original member 认真读一下上一句[/quote] jwj070524 贴的代码有部分是 ub,. 但是非ub的代码在g++上有正确的结果
iamnobody 2014-08-29
  • 打赏
  • 举报
回复
引用 16 楼 unituniverse2 的回复:
5.2.9.9 ISO/IEC 14882:2003(E) 5.2.9 Static cast 9.An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. 63)The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined.[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ] 虽然你觉得在这里gcc能给个确定的结果而vc却不能,但是UB就是UB,不存在谁对谁错的问题了。而你注意本人测试代码里连cast都没有用到,直接编译就通过了,gcc和vc结果也一致
你看错了,不是ub, or is a base or derived class of the class containing the original member 认真读一下上一句
unituniverse2 2014-08-29
  • 打赏
  • 举报
回复
现在的代码是严格按你贴出的标准中所描述的已有定义的情况。而需要注意的还有标准中所说的"rvalue of type(类型的右值,而不是左值)":

class B
{
public:
	B(void) : m("bbbb") {};

	void foo(void) const { std::cout << m << std::endl; };

	std::string m;
};

class D : public B
{
public:
	D(void) : m("dddd") {};

	std::string m;
};

int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
{
	D o1;
	B * p = &o1;
	void (B::* p1)(void) const;
	void (D::* p2)(void) const;

	p2 = &D::foo;

	p1 = &D::foo; // rvalue. OK.
	//p1 = p2; // lvalue. Error.
	(p->*p1)();

	_getch();
	return 0;
}
j8daxue 2014-08-29
  • 打赏
  • 举报
回复
引用 14 楼 mingliang1212 的回复:
[quote=引用 12 楼 jwj070524 的回复:] 接楼上。上面的代码是用g++跑的,我在vs2010上测试得到不一样的结果,这次的输出全是错误的值,-858993460的十六进制形式是0xCCCCCCCC,正好是vs2010 Debug版本中的未初始化内存区域的内容。所以这个特性不同的编译器有不同的实现啊。

sizeof(C) = 12
sizeof(B) = 4
p1: 0037FCE8
C this 0037FCE8, data -858993460
p2: 0037FCCC
C this 0037FCCC, data -858993460
请按任意键继续. . .
嗯,G++ 的实现是对的, 的确应该调整this指针的位置, VS的实现是错的,太坑了.[/quote] 作为没有虚函数的继承结构,使用普通成员函数指针访问子类的成员而自己不做手动偏移。 我觉得VS实现倒正常。
unituniverse2 2014-08-29
  • 打赏
  • 举报
回复
5.2.9.9 ISO/IEC 14882:2003(E) 5.2.9 Static cast 9.An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. 63)The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined.[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ] 虽然你觉得在这里gcc能给个确定的结果而vc却不能,但是UB就是UB,不存在谁对谁错的问题了。而你注意本人测试代码里连cast都没有用到,直接编译就通过了,gcc和vc结果也一致
unituniverse2 2014-08-29
  • 打赏
  • 举报
回复
引用 14 楼 mingliang1212 的回复:
[quote=引用 12 楼 jwj070524 的回复:] 接楼上。上面的代码是用g++跑的,我在vs2010上测试得到不一样的结果,这次的输出全是错误的值,-858993460的十六进制形式是0xCCCCCCCC,正好是vs2010 Debug版本中的未初始化内存区域的内容。所以这个特性不同的编译器有不同的实现啊。

sizeof(C) = 12
sizeof(B) = 4
p1: 0037FCE8
C this 0037FCE8, data -858993460
p2: 0037FCCC
C this 0037FCCC, data -858993460
请按任意键继续. . .
嗯,G++ 的实现是对的, 的确应该调整this指针的位置, VS的实现是错的,太坑了.[/quote] 开始也觉得是vs的问题,可是拷下来测一下才发现:你这3个指针指向的都不是同一个函数啊。 成员函数指针能在继承关系的类间转换没错,但是并没说能让基类指针引用派生类的不在基类存在的函数且允许被调用 用下面的代码测了一下,没发现什么问题:


class A
{
public:
	A(void) : m("aaaa") {};

	void f1(void) const
	{
		std::cout << m << std::endl;
	};

	virtual void f2(void) const
	{
		std::cout << m << std::endl;
	};

	std::string m;
};

class B1 : public A
{
public:
	B1(void) : m("bbbb1") {};

	std::string m;
};

class B2 : public A
{
public:
	B2(void) : m("bbbb2") {};

	void f2(void) const
	{
		std::cout << m << std::endl;
	};

	std::string m;
};

class C1
{
public:
	C1(void) : m("cccc1") {};

	void f3(void) const
	{
		std::cout << m << std::endl;
	};

	virtual void f2(void) const
	{
		std::cout << m << std::endl;
	};

	std::string m;
};

class D : public A, public C1
{
public:
	D(void) : m("dddd") {};

	void f2(void) const
	{
		std::cout << m << std::endl;
	};

	std::string m;
};


int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
{
	{
		B1 o1;
		A * pa = &o1;

		void (A::* p1)(void) const = &B1::f1;
		(pa->*p1)();
	}
	{
		B2 o1;
		A * pa = &o1;

		void (A::* p1)(void) const = &A::f2;
		(pa->*p1)();
	}
	{
		D o1;
		A * pa = &o1;
		C1 * pc = &o1;

		void (A::* p1)(void) const = &A::f2;
		void (C1::* p2)(void) const = &D::f3;
		(pa->*p1)();
		(pc->*p2)();
		p2 = &C1::f2;
		(pc->*p2)();
	}

	_getch();
	return 0;
}

jwj070524 2014-08-28
  • 打赏
  • 举报
回复
接楼上。上面的代码是用g++跑的,我在vs2010上测试得到不一样的结果,这次的输出全是错误的值,-858993460的十六进制形式是0xCCCCCCCC,正好是vs2010 Debug版本中的未初始化内存区域的内容。所以这个特性不同的编译器有不同的实现啊。

sizeof(C) = 12
sizeof(B) = 4
p1: 0037FCE8
C this 0037FCE8, data -858993460
p2: 0037FCCC
C this 0037FCCC, data -858993460
请按任意键继续. . .
jwj070524 2014-08-28
  • 打赏
  • 举报
回复
我发现一个奇怪的现象,当父类调用子类的成员函数时,比如之前的(pb->*pbm)(); 尽管主观上认为是父类指针(pb)调用子类的方法(pbm所指向的地址,强制类型转换之后),在子类的方法中this指针应该和pb的值相等。但是我实际测试发现this指针竟然与&ci等价,也就是pb根本没有用上。接下来又写了些代码测试,得到当父类调用子类的成员函数时,子类成员函数中的this指针会根据父类的类型自动减去一个偏移量调整成子类指针(转化后的指针也许是错误的,但是编译器的确这么做了)。测试代码

#include <stdio.h>
class A
{
public:
  A():data(1){}

  void afun()
  {
    printf("A this %p, data %d\n", (void*)this, data);
  }
  int data;
};

class B
{
public:
  B():data(2){}

  void bfun()
  {
    printf("B this %p, data %d\n", (void*)this, data);
  }
  int data;
};

class C:public A,public B
{
public :
  C():data(3){}

  void cfun()
  {
    printf("C this %p, data %d\n", (void*)this, data);
  }
  int data;
};


int main()
{
  printf("sizeof(C) = %u\n", sizeof(C));
  printf("sizeof(B) = %u\n", sizeof(B));
  C ci;
  int arr[1] = {98765};
  B bi;

  B *p1 = &ci;
  B *p2 = &bi;

  void (A::*fpa)() = &A::afun;
  void (B::*fpb)() = &B::bfun;
  void (C::*fpc)() = &C::cfun;

  {
    void (B::*fp)() = static_cast<void (B::*)()>(fpc);
    printf("p1: %p\n", (void*)p1);
    (p1->*fp)(); // output is 3 !!!
  }

  {
    void (B::*fp)() = static_cast<void (B::*)()>(fpc);
    printf("p2: %p\n", (void*)p2);
    (p2->*fp)(); // output is 98765 ???
  }

  return 0;
};
结果

[root@localhost src]# ./a.out
sizeof(C) = 12
sizeof(B) = 4
p1: 0xbfa03040
C this 0xbfa0303c, data 3
p2: 0xbfa03034
C this 0xbfa03030, data 98765
taodm 2014-08-28
  • 打赏
  • 举报
回复
那你还是继续啃标准吧。帮不了你。
iamnobody 2014-08-28
  • 打赏
  • 举报
回复
引用 8 楼 taodm 的回复:
static_cast和reinterpret_cast的功能本来就没有包含关系,只是有交集。
这个成员函数指针转换的条件 是包含关系
taodm 2014-08-28
  • 打赏
  • 举报
回复
static_cast和reinterpret_cast的功能本来就没有包含关系,只是有交集。
iamnobody 2014-08-28
  • 打赏
  • 举报
回复
引用 5 楼 jwj070524 的回复:
[quote=引用 4 楼 mingliang1212 的回复:] [quote=引用 3 楼 jwj070524 的回复:] 我用g++编译通过
跑一下结果对吗?[/quote] 结果是ccccc[/quote] 上面发错了,,,


	C ci;
    ci.am = "aaaaa";
    ci.bm = "bbbbb";
    ci.cm = "ccccc";
    B * pb = &ci;
	A * pa = &ci;
 
    // case 1:     5.2.9.9 ISO/IEC 14882:2003(E) 
    void (A::*pam)() = &A::afun;
    void (C::*pcm)() = &C::cfun;
    void (B::*pbm)() = static_cast<void (B::*)()>(pcm); // VS2012 says: warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码
    (pb->*pbm)();
	void (A::*pam2)() = static_cast<void (A::*)()>(pcm);
	(pa->*pam2)(); 

iamnobody 2014-08-28
  • 打赏
  • 举报
回复
引用 5 楼 jwj070524 的回复:
[quote=引用 4 楼 mingliang1212 的回复:] [quote=引用 3 楼 jwj070524 的回复:] 我用g++编译通过
跑一下结果对吗?[/quote] 结果是ccccc[/quote] G++ 可能要改成下面这样才会出问题: C ci; ci.am = "aaaaa"; ci.bm = "bbbbb"; ci.cm = "ccccc"; B * pb = &ci; A * pa = &ci; // case 1: 5.2.9.9 ISO/IEC 14882:2003(E) void (A::*pam)() = &A::afun; void (C::*pcm)() = &C::cfun; void (B::*pbm)() = static_cast<void (B::*)()>(pcm); // VS2012 says: warning C4407: 在指向成员表示形式的不同指针之间进行转换,编译器可能生成不正确的代码 (pb->*pbm)(); void (A::*pam)() = static_cast<void (A::*)()>(pcm); (pa->*pam)();
jwj070524 2014-08-28
  • 打赏
  • 举报
回复
引用 4 楼 mingliang1212 的回复:
[quote=引用 3 楼 jwj070524 的回复:] 我用g++编译通过
跑一下结果对吗?[/quote] 结果是ccccc
iamnobody 2014-08-28
  • 打赏
  • 举报
回复
引用 3 楼 jwj070524 的回复:
我用g++编译通过
跑一下结果对吗?
jwj070524 2014-08-28
  • 打赏
  • 举报
回复
我用g++编译通过
我看你有戏 2014-08-28
  • 打赏
  • 举报
回复
一个是安全转换,一个不安全
幻夢之葉 2014-08-28
  • 打赏
  • 举报
回复
不明觉厉

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧