0

在不可修改的标头中声明了一个前向 C 结构。我想“虚拟地”向它添加便利的成员函数。显然,我的第一选择是扩展结构并将方法添加到派生类。不能做,因为结构本身在标题中被声明为“转发”,所以我收到错误“错误:不完整类型的无效使用......”。如果我尝试使用旧结构的单个元素定义新结构,我会收到类似的错误。这很糟糕。

然而,我在想我可以用 reinterpret_cast 做一些hacky来让它工作。它会走的路是这样的:

//defined in header
struct A forward;
void do_something_with_A(A* a, int arg);

//defined in my wrapper
struct B {
  B* wrap(A* a) {return reinterpret_cast<B*>(a); }
  void do_something(int arg) {do_something_with_A(reinterpret_cast<A*>(this),arg); }
}

如果我添加了从 B 类型到 A 类型的隐式转换,我认为这几乎可以像 B 是 A 的零数据继承者一样工作。但是,这显然提出了一个问题:这在 C++ 中是未定义的吗?通常我会认为访问非法转换结构的元素是未定义的;那讲得通。但是,我认为从一种类型到另一种类型的 reinterpret_casting,传递该指针,然后再次转换,中间不做任何非法的事情就可以了。我还认为编译器实现非虚拟结构成员的方式是创建一个函数

B::do_something(B* b, int arg)

并使用 B 的适当参数调用它。然后这会简化为前一种情况,根据我的可疑逻辑,这是可以的。所以我认为在一个实际上是 reinterpret_cast A 的结构上调用 .do_something 是可以的。

然而,这并没有说明 C++ 标准在这个问题上的实际说法。有什么帮助吗?此外,如果有人知道这将如何实际工作,(即“每个编译器都接受这个”,或“这只适用于少数编译器”)也会有所帮助,但稍微少一些。

4

2 回答 2

2

我相信,如果您将 A* 转换为 B*,然后再次将其转换回 A*,那么标准会说您没问题。这些将是 reinterpret_casts,但不是 static_casts。

但是正常的解决方案到底有什么问题呢?

class B
{
private:
  A* ptr;
public:
  B(A* p) : ptr(p) {}
  void do_something(int arg) { do_something_with_A(ptr,arg); }
};

似乎与您的解决方案一样有效,而且更少胡闹。

于 2011-08-24T04:34:45.247 回答
1

如果您正在使用,我不相信这会起作用,static_cast因为您不能static_cast在两个完全不相关的类类型之间。具体来说,如果您有一个类型的指针A*并尝试将其转换为一个类型的指针B*,则static_cast只有在此声明有效时才会成功:

B* ptr(myAPtr);

或者 ifB是非虚拟派生自A(它不是)。有关此细节的详细信息,请参阅 ISO 规范 §5.2.9。如果我们考虑上述声明,那么在所有 §4 中唯一可能应用的转换是 §4.10 中的转换,其中唯一可能适用的转换是从基类到派生类的转换(§4.10/3) ,但这不适用于此处,因为A并且B不是相关类型。

您可以在这里使用的唯一演员是 a reinterpret_cast,而且看起来这也不起作用。特别是,跨类层次结构的强制转换行为是(§5.2.10/7)

指向对象的指针可以显式转换为指向不同类型对象的指针。65)除了将“指向 T1 的指针”类型的右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型和在 T2 的对齐要求不比 T1 严格的情况下,返回其原始类型会产生原始指针值,这种指针转换的结果是未指定的。

因此,如果两个对象具有不同的对齐限制,则立即无法保证任何事情都会起作用,并且您无法确保这是真的。但假设你可以。不过,在这种情况下,我相信这实际上会正常工作!这是推理。当您调用对象的成员函数时B,规则 &5.2.2/1) 会启动并说明,因为该函数是非虚拟的:

[...]成员函数调用中调用的函数通常根据对象表达式的静态类型进行选择。[...]

好的,所以我们至少调用了正确的函数。现在,this指针呢?好吧,根据 &5.2.2/4:

[...] 如果函数是非静态成员函数,则函数的“this”参数(9.3.2)应使用指向调用对象的指针进行初始化,如同通过显式类型转换(5.4 )。[...]

最后一部分完成的类型转换是从 aB*到 a的标识转换B*,因为这是选定的类型。所以你已经调用了正确的函数,并正确this设置了指针。好的!最后,当您reinterpret_cast返回原始类型时,按照前面的规则,您将取回A*对象,一切都会按预期进行。

当然,这仅适用于对象具有相同对齐要求的情况,并且不能保证。因此,您不应该这样做!

希望这可以帮助!

于 2011-08-24T04:45:34.203 回答