在不可修改的标头中声明了一个正向C结构。我想“虚拟地”添加便利成员函数。显然,我的第一选择是扩展结构并将方法添加到派生类。不可以,因为结构本身在标题中被声明为“转发”,所以我得到错误“错误:无效使用不完整类型......”。如果我尝试使用旧结构的单个元素定义新结构,我会收到类似的错误。这很糟糕。
但是,我想我可以用reinterpret_cast做一些hackery来让它继续工作。它的方式是:
//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 ++标准在这个问题上的实际内容。对此有何帮助?此外,如果某人有关于这将如何实际工作的信息,(即“每个编译器都接受这个”,或“这只适用于一些编译器”),这也会有所帮助,但稍微不那么。
答案 0 :(得分: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); }
};
似乎与您的解决方案一样高效,而且不那么糟糕。
答案 1 :(得分:1)
如果您使用static_cast
我不相信这是有效的,因为您不能在两个完全不相关的类类型之间static_cast
。具体来说,如果您有一个类型为A*
的指针并尝试将其转换为类型为B*
的指针,则static_cast
只有在此声明有效时才会成功:
B* ptr(myAPtr);
或B
是否非虚拟派生自A
(事实并非如此)。有关详细信息,请参阅ISO规范§5.2.9。如果我们考虑上述声明,那么可以在§4中应用的唯一可能的转换是§4.10中的转换,其中唯一可能适用的转换是从基类到派生类的转换(§4.10/ 3) ,但这不适用于此处,因为A
和B
不是相关类型。
你可以在这里使用的唯一一个演员是reinterpret_cast
,看起来这也不会起作用。特别是,跨类层次结构的转换行为是(§5.2.10/ 7)
指向对象的指针可以显式转换为指向不同类型对象的指针.65)除了将“指向T1的指针”的rvalue转换为“指向T2的指针”的类型(其中T1和T2是对象类型,并且T2的对齐要求不比T1)更严格,并且回到原始类型会产生原始指针值,这种指针转换的结果是未指定的。
因此,如果两个对象具有不同的对齐限制,并且无法确保这是真的,那么立即无法确保任何事情都能正常工作。但是假设你可以。但是,在这种情况下,我相信这实际上会正常工作!这是推理。当您调用B
对象的成员函数时,则规则&amp; 5.2.2 / 1)启动并说,因为该函数是非虚拟的:
[...]成员函数调用中调用的函数通常根据对象表达式的静态类型进行选择。 [...]
好的,所以我们至少要调用正确的功能。现在,this
指针怎么样?好吧,根据&amp; 5.2.2 / 4:
[...]如果函数是非静态成员函数,则函数(9.3.2)的“this”参数应使用指向调用对象的指针进行初始化,并按照显式类型进行转换转换(5.4)。 [...]
在最后一部分中完成的类型转换是从B*
到B*
的身份转换,因为这是所选类型。所以你调用了正确的函数,并适当地设置了this
指针。太好了!最后,当您执行reinterpret_cast
回到原始类型时,按照之前的规则,您将返回A*
对象,一切都将按预期进行。
当然,仅在对象具有相同的对齐要求时才有效,并且无法保证。因此,你不应该这样做!
希望这有帮助!