c++ reinterpret_cast、virtual 和 templates 可以吗?

发布于 2024-12-28 20:47:44 字数 1814 浏览 0 评论 0原文

在 C++ 中,假设以下类层次结构:

class BaseClass { };
class ChildClass : public BaseClass { };

进一步假设这两个类的工厂类具有公共模板化基类:

template<typename T>
class Factory {
public:
  virtual T* create() = 0;
};

class BaseClassFactory : public Factory<BaseClass> {
public:
  virtual BaseClass* create() {
    return new BaseClass(&m_field);
  }
private:
  SomeClass m_field;
};

class ChildClassFactory : public Factory<ChildClass> {
public:
  virtual ChildClass* create() {
    return new ChildClass(&m_field);
  }
private:
  SomeOtherClass m_field; // Different class than SomeClass
};

请注意,ChildClassFactoryBaseClassFactory 的大小/内部结构为因领域不同而不同。

现在,如果 a 有一个 ChildClassFactory (或 Factory)的实例,我可以安全地将其转换为 Factory (通过reinterpret_cast)?

Factory<ChildClass>* childFactory = new ChildClassFactory();

// static_cast doesn't work - need to use reinterpret_cast
Factory<BaseClass>* baseFactory = reinterpret_cast<Factory<BaseClass>*>(childFactory);

// Does this work correctly? (i.e. is "cls" of type "ChildClass"?)
BaseClass* cls = baseFactory->create();

我知道您不能总是以这种方式强制转换模板类,但在这种特殊情况下强制转换应该是安全的,不是吗?

我已经用 Visual C++ 2010 对其进行了测试,它确实有效。我现在的问题是这是否可以移植到其他编译器?

更新:由于存在一些混乱,让我澄清一些在我的示例中(应该)重要的内容:

  • ChildClassBaseClass 的子类code>
  • Factory 的用户不知道 BaseClass 将创建哪个子类。他只知道 BaseClass 已创建。
  • Factory 没有自己的字段(除了 vtable 之外)。
  • Factory::create()虚拟

In C++, assume following class hierarchy:

class BaseClass { };
class ChildClass : public BaseClass { };

Further assume factory classes for these two classes with a common, templated base class:

template<typename T>
class Factory {
public:
  virtual T* create() = 0;
};

class BaseClassFactory : public Factory<BaseClass> {
public:
  virtual BaseClass* create() {
    return new BaseClass(&m_field);
  }
private:
  SomeClass m_field;
};

class ChildClassFactory : public Factory<ChildClass> {
public:
  virtual ChildClass* create() {
    return new ChildClass(&m_field);
  }
private:
  SomeOtherClass m_field; // Different class than SomeClass
};

Note that the size/internal structure of ChildClassFactory and BaseClassFactory is different due to their different fields.

Now, if a have an instance of ChildClassFactory (or Factory<ChildClass>), can I safely cast it to Factory<BaseClass> (via reinterpret_cast)?

Factory<ChildClass>* childFactory = new ChildClassFactory();

// static_cast doesn't work - need to use reinterpret_cast
Factory<BaseClass>* baseFactory = reinterpret_cast<Factory<BaseClass>*>(childFactory);

// Does this work correctly? (i.e. is "cls" of type "ChildClass"?)
BaseClass* cls = baseFactory->create();

I know that you can't always cast templated classes this way, but in this special case a cast should be safe, shouldn't it?

I've tested it with Visual C++ 2010 and it does work. My question now is whether this is portable to other compilers?

Update: Since there has been some confusion let me clarify some more what's (supposed to be) important in my example:

  • ChildClass is a child class of BaseClass
  • A user of Factory<BaseClass> doesn't know what child class of BaseClass will be created. All he knows is that BaseClass is created.
  • Factory<T> has no fields of its own (other than the vtable).
  • Factory::create() is virtual

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

殤城〤 2025-01-04 20:47:44

不,事实并非如此。除了将内容强制转换回来之外,您不得使用 reinterpret_cast 的结果,但少数特殊情况除外:

ISO14882:2011(e) 5.2.10-7:

对象指针可以显式转换为对象指针
不同的类型。70 当“指向 T1 的指针”类型的纯右值 v 为
转换为“指向 cv T2 的指针”类型,如果 T1 和 T2 都是标准布局,则结果为 static_cast(static_cast(v))
类型(3.9)和 T2 的对齐要求不比
T1 的那些,或者任一类型无效。转换类型的纯右值
“指向 T1 的指针”到“指向 T2 的指针”类型(其中 T1 和 T2 是
对象类型并且没有 T2 的对齐要求
比 T1 更严格)并返回其原始类型,得出
原始指针值。任何其他此类指针的结果
转换未指定。

为了使可能的失败场景更清楚,请考虑多重继承,其中使用 static_castdynamic_cast 有时会调整指针值,但 reinterpret_cast 会不是。考虑在此示例中从 A* 转换为 B*

struct A { int x; };
struct B { int y; };
struct C : A, B { };

要了解代码如何以不同的方式失败,请考虑大多数编译器如何实现虚拟函数调用机制:虚拟指针。您的 ChildClassFactory 实例将有一个虚拟指针,指向 ChildClassFactory 的虚拟表。现在,当您reinterpret_cast这个野兽时,它只是偶然“工作”,因为编译器需要一些虚拟指针,指向具有相同/相似布局的虚拟表。但它仍然会包含指向ChildCLassFactory虚拟函数的值,因此这些函数将被调用。所有这些都是在调用未定义行为之后很长一段时间发生的。就好像您开着车跳进一个大峡谷,并想着“嘿,一切都很好”,只是因为您还没有落地。

No, it is not. You may not use the result of a reinterpret_cast other than to cast stuff back, except for a few special cases:

ISO14882:2011(e) 5.2.10-7:

An object pointer can be explicitly converted to an object pointer of
a different type.70 When a prvalue v of type “pointer to T1” is
converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout
types (3.9) and the alignment requirements of T2 are no stricter than
those of T1, or if either type is void. Converting a prvalue of type
“pointer to T1” to the type “pointer to T2” (where T1 and T2 are
object types and where the alignment requirements of T2 are no
stricter than those of T1) and back to its original type yields the
original pointer value. The result of any other such pointer
conversion is unspecified.

To make a possible failure scenario more clear, consider multiple inheritance, where using a static_cast or dynamic_cast would sometimes adjust the pointer value, but reinterpret_cast will not. Consider casting in this example from A* to B*:

struct A { int x; };
struct B { int y; };
struct C : A, B { };

To understand how your code fails in a different way too, consider how most compilers implement the virtual function call mechanism: With virtual pointers. Your instance of ChildClassFactory will have a virtual pointer, pointing to the virtual table of ChildClassFactory. Now when you reinterpret_cast this beast, it just happens to "work" incidentally, because the compiler expects some virtual pointer, pointing to a virtual table that will have the same/similar layout. But it will still contain the values pointing to the ChildCLassFactory virtual functions, thus these functions would be called. All of this is long after invoking undefined behaviour. It is as if you are jumping with a car into a large canyon and thinking "hey, everything is driving fine" just because you have not hit the ground yet.

触ぅ动初心 2025-01-04 20:47:44

不,reinterpret_cast 只能用于低级代码,因为它不会执行正确的地址操作。使用 static_cast 或dynamic_cast 代替,

为什么你想要两个工厂,这不适合 GoF 工厂模式。

reinterpret_cast 不是这样做的方法,因为它很慢(运行时检查)并且不是一个很好的 OO 设计(您想要使用语言中的多态性构建)。

相反,在工厂类中创建生成所需类型的构造函数,然后让它们调用各个类型的构造函数。

工厂模式允许您忽略实现中的更改,这是一件好事,因为您可以最大限度地减少依赖关系,并且可以在将来更轻松地维护代码。

No, reinterpret_cast is only to be used for lowlevel code since it will not perfrom the correct address manipulation. Use static_cast or dynamic_cast instead,

Why do you want two factories this does not fit in the GoF factory pattern.

reinterpret_cast is not the way to do it since it is slow (runtime checks) and is not a nice OO design (you want to use the polymophisme build into the language).

Instead make constructors in the factory class that produces the types you are after and then have these call the constructor of the individual types.

The factory pattern allows you to be by ignorant of changes in your implementaion, which is a good thing since you minimize your dependencies, and allows for easier maintainance in the future of the code.

暮年 2025-01-04 20:47:44

我已经勾选了上面的原始答案(以表彰他),但我想我应该总结一下我在这里学到的东西。

因此,基本问题是没有定义如何实现分派虚拟调用。

这意味着内部用于虚拟呼叫分派的数据结构(例如,vtable)可能在从​​同一模板创建的模板实例之间位兼容,也可能不兼容。

I've ticked the original answer above (to give him the credit), but I thought I'd sum up what I've learned here.

So, the basic problem is that it's not defined how dispatching virtual calls must be implemented.

This means that the data structure(s) that are internally used for virtual call dispatching (e.g. vtables) may or may not be bit compatible among template instantiations created from the same template.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文