确保 dynamic_cast 不会导致未定义的行为 C++
Make sure that dynamic_cast won't cause undefined behaviour C++
直到今天我还认为如果我有 class A
和 B
没有任何关系,使用指针的 dynamic_cast
将无法返回nullptr
。现在我读到这会导致未定义的行为...
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
键入
- 等于
ActorComponent
- 源自
ActorComponent
...甚至更多。
问题是:如何在编译时确保 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
) 是一个指针,它没有构造函数。顺便说一句,它是未初始化的,所以你的代码无论如何都是完全非法的,但不是因为你问的原因。
直到今天我还认为如果我有 class A
和 B
没有任何关系,使用指针的 dynamic_cast
将无法返回nullptr
。现在我读到这会导致未定义的行为...
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. Ifnew_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
键入
- 等于
ActorComponent
- 源自
ActorComponent
...甚至更多。
问题是:如何在编译时确保 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
) 是一个指针,它没有构造函数。顺便说一句,它是未初始化的,所以你的代码无论如何都是完全非法的,但不是因为你问的原因。