调用从不兼容类型转换而来的零数据结构的成员函数 - 未定义?
在不可修改的标头中声明了一个前向 C 结构。我想“虚拟地”为其添加方便的成员函数。显然,我的第一选择是扩展结构并将方法添加到派生类。无法做到,因为结构本身在标头中被声明为“forward”,所以我收到错误“错误:无效使用不完整类型...”。如果我尝试使用旧结构的单个元素定义新结构,我会收到类似的错误。这太糟糕了。
然而,我想我可以用reinterpret_cast做一些黑客操作来让它工作。事情的发展方向是这样的:
//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++ 标准在这个问题上的实际含义。有什么帮助吗?另外,如果有人知道这在实际中效果如何(即“每个编译器都接受这个”,或“这仅适用于少数编译器”),这也会有帮助,但稍微没那么有用。
There is a forward C struct declared in an unmodifiable header. I would like to "virtually" add convenience member functions to it. Obviously my first choice would be to extend the struct and add the methods to the derived class. No-can-do, as the struct itself is declared as "forward" in the header, so I get the error "error: invalid use of incomplete type ...". I get a similar error if I try to define a new struct with a single element of the old struct. This sucks.
However, I was thinking that I could do some hackery with reinterpret_cast to get it to work anyway. The way it would go is this:
//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); }
}
If I added implicit conversions from type B to type A, I was thinking that this could work out almost as if B was a zero-data inheritor of A. However, this obviously brings up the question: is this undefined in C++? Normally I would think that accessing an element of an illegally casted struct would be undefined; that makes sense. However, I would think that reinterpret_casting from one type to another, passing that pointer around, and then casting back again, without doing anything illegal in between would be fine. I would also think that the way a compiler implements non-virtual struct members would be creating a function
B::do_something(B* b, int arg)
and calling that with the appropriate argument for B. This then reduces to the previous case, which by my dubious logic is okay. So I would think calling .do_something on a struct which is actually a reinterpret_cast A would be okay.
However, this says nothing for what the C++ standard actually says on the matter. Any help with that? Also, if someone has information on how well this will work practically, (i.e. "Every compiler ever made accepts this", or "this only works with a few compilers") that would also be helpful, but slightly less so.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我相信,如果您将 A* 转换为 B*,然后再次将其转换回 A*,那么标准表明您没问题。那些将是reinterpret_casts,但不是static_casts。
但正常的解决方案到底有什么问题呢?
似乎与您的解决方案一样有效并且更少杂乱。
I believe that if you cast A* to B* and then cast it back to A* again, then the standard says you are OK. Those would be reinterpret_casts though not static_casts.
But what exactly is wrong with the normal solution?
Seems to be as efficient as your solution and less mucking about.
我不认为如果您使用
static_cast
,这会起作用,因为您无法在两个完全不相关的类类型之间进行static_cast
。具体来说,如果您有一个A*
类型的指针并尝试将其转换为B*
类型的指针,则仅static_cast
如果此声明有效,则成功:或者如果
B
不是从A
虚拟派生的(但事实并非如此)。有关详细信息,请参阅 ISO 规范第 §5.2.9 节。如果我们考虑上述声明,则在所有 §4 中唯一可能应用的转换是 §4.10 中的转换,而其中唯一可能适用的转换是从基类到派生类的转换 (§4.10/3) ,但这不适用于此处,因为A
和B
不是相关类型。您在这里可以使用的唯一转换是
reinterpret_cast
,而且看起来这也不起作用。特别是,跨类层次结构的转换行为是(§5.2.10/7)因此,如果两个对象具有不同的对齐限制,则无法立即保证任何操作都能正常工作,并且您无法确保这是真的。但假设你可以。但在这种情况下,我相信这实际上会正常工作!这是推理。当您调用
B
对象的成员函数时,规则 &5.2.2/1) 就会启动并说明这一点,因为该函数是非虚拟的:好的,所以我们至少调用了正确的函数。现在,
this
指针怎么样?嗯,根据&5.2.2/4:最后一部分完成的类型转换是从
B*
到B*
的恒等转换,因为这是选定的类型。因此,您已经使用适当设置的this
指针调用了正确的函数。好的!最后,当您通过reinterpret_cast
返回原始类型时,根据之前的规则,您将返回A*
对象,一切都会按预期进行。当然,只有当对象具有相同的对齐要求时才有效,并且不能保证这一点。因此,您不应该这样做!
希望这有帮助!
I don't believe this works if you're using
static_cast
because you cannotstatic_cast
between two completely unrelated class types. To be specific, if you have a pointer of typeA*
and try to convert it to a pointer of typeB*
, thestatic_cast
only succeeds if this declaration is valid:or if
B
is non-virtually derived fromA
(which it isn't). See the ISO spec §5.2.9 for details on the specifics of this. If we consider the above declaration, the only possible conversions that could be applied here in all of §4 are those in §4.10, and of those the only one that might be applicable is conversion from base to derived classes (§4.10/3), but this doesn't apply here becauseA
andB
aren't related types.The only cast you might be able to use here is a
reinterpret_cast
, and it doesn't look like this will work either. In particular, the behavior of casting across class hierarchies is (§5.2.10/7)So immediately there's no guarantee that anything is going to work if the two objects have different alignment restrictions, and you cannot ensure that this is true. But suppose that you could. In that case, though, I believe that this will actually work correctly! Here's the reasoning. When you call the member function of the
B
object, then rule &5.2.2/1) kicks in and says that, since the function is nonvirtual:Okay, so we're at least calling the right function. Now, what about the
this
pointer? Well, according to &5.2.2/4:The type conversion done in the last part is the identity conversion from a
B*
to aB*
, since that's the selected type. So you've called the right function with thethis
pointer set appropriately. Nice! Finally, when you do areinterpret_cast
back to the original type, by the earlier rule, you'll get back theA*
object and everything will go as expected.Of course, this only works if the objects have the same alignment requirements, and this cannot be guaranteed. Consequently, you shouldn't do it!
Hope this helps!