大家晚上好。
一段代码片段胜过千言万语:
如您所见,我只想通过基类访问实例,而不实际存储基类指针。请注意,在第二部分中,“Derived2”类型信息将丢失,因此我只剩下“storage”和一个派生实例的保证。
由于放置new从不调整目标指针,这归结为使用reinterpret_cast向上转换为基类。现在我知道这很危险,因为更合适的static_cast在某些情况下会调整指针。[1]
事实上,它会触发未定义行为。您可以在Coliru上找到完整的代码here on Coliru(g++ 4.9.0),其中它会立即在运行时崩溃。与此同时,在我的PC上(g++ 4.8.2),一切正常。请注意,在g++ 4.9上,在调用函数之前输出两个指针显示相同的值...并且有效。
所以我试图从反面考虑问题:推动派生实例,使指向
没有运气。这个东西在g++ 4.8上仍然很好,但在g++ 4.9上失败了。
编辑:想一想,上面的方法并不那么聪明。因为,如果派生实例最终出现在
所以我的问题是:是否有解决我正在尝试实现的问题的方法?或者,[1]中提到的情况是否已经被充分定义,我可以编写适用于一些类的代码(例如,没有虚拟继承)?
一段代码片段胜过千言万语:
// Storage suitable for any of the listed instances
alignas(MaxAlign<Base, Derived1, Derived2>::value)
char storage[MaxSize<Base, Derived1, Derived2>::value];
// Instanciate one of the derived classes using placement new
new (storage) Derived2(3.14);
// Later...
// Recover a pointer to the base class
Base &ref = *reinterpret_cast<Base*> (storage);
// Use its (virtual) functions
ref.print();
// Destroy it when we're done.
ref.~Base();
如您所见,我只想通过基类访问实例,而不实际存储基类指针。请注意,在第二部分中,“Derived2”类型信息将丢失,因此我只剩下“storage”和一个派生实例的保证。
由于放置new从不调整目标指针,这归结为使用reinterpret_cast向上转换为基类。现在我知道这很危险,因为更合适的static_cast在某些情况下会调整指针。[1]
事实上,它会触发未定义行为。您可以在Coliru上找到完整的代码here on Coliru(g++ 4.9.0),其中它会立即在运行时崩溃。与此同时,在我的PC上(g++ 4.8.2),一切正常。请注意,在g++ 4.9上,在调用函数之前输出两个指针显示相同的值...并且有效。
所以我试图从反面考虑问题:推动派生实例,使指向
Base
的指针等于 storage
。void *ptr = static_cast<Derived2*>(reinterpret_cast<Base*>(storage));
new (ptr) Derived2(3.14);
没有运气。这个东西在g++ 4.8上仍然很好,但在g++ 4.9上失败了。
编辑:想一想,上面的方法并不那么聪明。因为,如果派生实例最终出现在
storage
之前...糟糕。所以我的问题是:是否有解决我正在尝试实现的问题的方法?或者,[1]中提到的情况是否已经被充分定义,我可以编写适用于一些类的代码(例如,没有虚拟继承)?
new
表达式中检索出Base*
,并将其存储以供以后使用-Base *ref = new (storage) Derived2(3.14);
-这避免了显式强制转换。即使进行了此更改,代码仍会崩溃,我认为这可能是由于此错误引起的。 - Praetorianstatic_assert
来确保指针是相同的,并继续进行。你应该发表有关g++错误的答案,这样我可以接受它。 - Quentin