确保 dynamic_cast 不会导致未定义的行为 C++

Make sure that dynamic_cast won't cause undefined behaviour C++

直到今天我还认为如果我有 class AB 没有任何关系,使用指针的 dynamic_cast 将无法返回nullptr。现在我读到这会导致未定义的行为...

cppreference - dynamic_cast:

When dynamic_cast is used in a constructor or a destructor (directly or indirectly), and expression refers to the object that's currently under construction/destruction, the object is considered to be the most derived object. If new_type is not a pointer or reference to the constructor's/destructor's own class or one of its bases, the behavior is undefined.

我有这样的代码:

class ActorComponent
{
    ActorComponent* m_parent;
public:
    template <typename _type>
    _type* getParent() const {
        return dynamic_cast<_type*>(m_parent);
    }

    ActorComponent(ActorComponent* parent) {
        // using `getParent` inside the constructor:
        auto p = this->getParent<int>(); // `int` obviously isn't base of the ActorComponent
    }
};

我需要限制 _type 键入

...甚至更多。

问题是:如何在编译时确保 dynamic_cast 到这样的 _type 不会导致未定义的行为?会简单

template <typename _type,
    typename = std::enable_if_t< std::is_base_of_v<ActorComponent, std::remove_cv_t<_type> > || std::is_same_v<ActorComponent, std::remove_cv_t<_type> > >

总是工作?

tl;dr 没关系。出于某种原因,您选择了一个显然不适用于您的代码的条件。


您的报价:

If new_type is not a pointer or reference to the constructor's/destructor's own class or one of its bases, the behavior is undefined.

显然是关于在构造函数或析构函数中使用 dynamic_cast,尽管出于某种原因您省略了完整的上下文:

Only the following conversions can be done with dynamic_cast, ...

1) ... 5) ...

6) When dynamic_cast is used in a constructor or a destructor (directly or indirectly), and expression refers to the object that's currently under construction/destruction, the object is considered to be the most derived object. If new_type is not a pointer or reference to the constructor's/destructor's own class or one of its bases, the behavior is undefined.

由于您没有在构造函数或析构函数中使用它,情况 6 显然无关紧要。

与您的代码实际相关的案例是:

5) If expression is a pointer or reference to a polymorphic type Base, and new_type is a pointer or reference to the type Derived a run-time check is performed:

a) The most derived object pointed/identified by expression is examined. If, in that object, expression points/refers to a public base of Derived, and if only one subobject of Derived type is derived from the subobject pointed/identified by expression, then the result of the cast points/refers to that Derived subobject. (This is known as a "downcast".)

b) Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".) c) Otherwise, the runtime check fails. If the dynamic_cast is used on pointers, the null pointer value of type new_type is returned. If it was used on references, the exception std::bad_cast is thrown.


查看后面编辑的代码:

ActorComponent(ActorComponent* parent) {
    // using `getParent` inside the constructor:
    // `int` obviously isn't base of the ActorComponent
    auto p = this->getParent<int>();
}

条款6条件的第一部分

When dynamic_cast is used in a constructor or a destructor (directly or indirectly) ...

遇到的(getParent代码间接在一个构造函数中),但是第二部分:

... and expression refers to the object that's currently under construction/destruction ...

满足,所以第6条仍然不适用。

您正在转换 m_parent,它是您正在构建的 ActorComponent 对象的成员子对象。由于成员子对象在您进入构造函数的主体时已完全构造,因此 that 对象 not 正在构造并且我们不在 那个对象的构造函数。撇开其他不谈,那个对象 (m_parent) 是一个指针,它没有构造函数。顺便说一句,它是未初始化的,所以你的代码无论如何都是完全非法的,但不是因为你问的原因。