【发布时间】:2015-01-26 18:28:39
【问题描述】:
使用指向基类的指针调用类的虚成员函数在 C++ 中当然是很常见的事情。所以我觉得奇怪的是,当你有一个成员指针而不是普通指针时,似乎不可能做同样的事情。请考虑以下代码:
struct B
{
virtual void f();
};
struct D : B
{
virtual void f();
};
struct E
{
B b;
D d;
};
int main()
{
E e;
// First with normal pointers:
B* pb1 = &e.b; // OK
B* pb2 = &e.d; // OK, B is a base of D
pb1->f(); // OK, calls B::f()
pb2->f(); // OK, calls D::f()
// Now with member pointers:
B E::* pmb1 = &E::b; // OK
B E::* pmb2 = &E::d; // Error: invalid conversion from ‘D E::*’ to ‘B E::*’
(e.*pmb1).f(); // OK, calls B::f()
(e.*pmb2).f(); // Why not call D::f() ???
return 0;
}
Visual C++ 继续说:
error C2440: 'initializing' : cannot convert from 'D E::* ' to 'B E::* '
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
我不明白为什么这些是“无关的”。为什么这不可能?
编辑:
我试图保持这是一个 C++ 问题,而不是关于我要解决的特定问题,但这本质上是我想要做的:
std::vector<B E::*> v;
v.push_back( &E::b ); // OK
v.push_back( &E::d ); // Error
B& g( E& e, int i )
{
return e.*v[i];
}
E 是一个包含多个从 B 派生的成员的类。向量 v 用于组织(例如重新排序)指向这些成员的成员指针。向量 v 不经常变化。函数 g() 允许您使用 v 中的索引来选择 E 的成员之一。它被非常频繁地调用,并且每次都使用不同的 E。
如果你仔细想想,v 只是一个偏移量查找表。函数 g() 只需选择其中一个偏移量并将其添加到 E* 以返回 B*。函数 g() 被编译器内联,只编译成 4 个 CPU 指令,这正是我想要的:
// g( e, 1 )
mov rax,qword ptr [v (013F7F5798h)]
movsxd rcx,dword ptr [rax+4]
lea rax,[e]
add rcx,rax
我想不出标准为什么不允许将 D E::* 转换为 B E::* 的任何原因。
【问题讨论】:
-
您是在询问有关 C++ 设计的信息,还是要求您了解不允许这样做的标准部分?或者你有一个具体的问题想要解决,并且正在以一种间接的方式寻求解决方案?
-
嗨@Yakk,我在标准中找不到任何说明这是不允许的,所以如果它在那里,如果你能指出我将不胜感激。但我也想知道为什么不允许这样做,因为这似乎是一件合理的事情。我想不出编译器有什么问题。
-
也许我错了,但成员变量的动态类型不是在编译时总是已知的吗?真的有一个合理的场景,你会实际使用它吗?
-
@MikeMB,我在原始问题中添加了更多信息。在这方面,我认为成员指针与常规指针没有任何不同——我认为在这两种情况下,编写通过指向公共基类的指针集合来操作不同对象的代码是很有用的。我仍然有兴趣了解为什么标准中禁止这样做,因为我很想尝试 Visual C++ 错误消息中的 reinterpret_cast“推荐”。
-
@Barnett 对于它的价值:我找不到任何技术原因会阻止该语言实现您正在寻找的东西。可悲的是,数据指针的成员是所有数据类型中最不支持的。
标签: c++