到目前为止,我认为如果我的课程A
和B
之间没有任何关联,dynamic_cast
使用指针将无法返回nullptr
。现在我已经读到这会导致未定义的行为...
当构造函数或析构函数中使用
dynamic_cast
(直接或间接),并且表达式引用当前正在构造/销毁的对象时,该对象被认为是派生最多的对象。如果new_type
不是构造函数/析构函数自己的类或其基础之一的指针或引用,则行为未定义。
我有这样的代码:
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> > >
总是有效吗?
答案 0 :(得分:3)
tl; dr 没关系。出于某种原因,您选择的条件显然不适用于您的代码。
你的引言:
如果new_type不是构造函数/析构函数自己的类或其基础的指针或引用,则行为未定义。
显然是在构造函数或析构函数中使用dynamic_cast
,尽管由于某种原因你省略了完整的上下文:
使用dynamic_cast只能进行以下转换,...
1)...... 5)......
6)当构造函数或析构函数(直接或间接)使用dynamic_cast时,表达式引用当前正在构造/销毁的对象,该对象被认为是派生最多的对象。如果new_type不是指向构造函数/析构函数自己的类或其基础的指针或引用,则行为是未定义的。
由于你没有在构造函数或析构函数中使用它,因此案例6显然是无关紧要的。
与您的代码实际相关的案例是:
5)如果expression是多态类型Base的指针或引用,new_type是Derived类型的指针或引用,则执行运行时检查:
a)检查由表达式指向/识别的最派生对象。如果在该对象中,表达式指向/指向Derived的公共基础,并且如果只有一个派生类型的子对象是从由表达式指向/标识的子对象派生的,则转换结果/指向该派生子对象。 (这被称为“垂头丧气”。)
b)否则,如果表达式points /引用最派生对象的公共基础,并且同时,最派生的对象具有Derived类型的明确公共基类,则转换点的结果/指的是派生(这被称为“侧面”。) c)否则,运行时检查失败。如果在指针上使用dynamic_cast,则返回new_type类型的空指针值。如果它在引用上使用,则抛出异常std :: bad_cast。
检查稍后编辑的代码:
ActorComponent(ActorComponent* parent) {
// using `getParent` inside the constructor:
// `int` obviously isn't base of the ActorComponent
auto p = this->getParent<int>();
}
第6条第一部分的条件
在构造函数或析构函数中使用dynamic_cast(直接或间接)...
符合(getParent
代码间接在构造函数中),但第二部分:
...而表达式指的是当前正在构建/破坏的对象......
不满意,因此第6条仍然不适用。
您正在投射m_parent
,它是您正在构建的ActorComponent
对象的成员子对象。由于成员子对象是在您进入构造函数体时完全构造的, 对象不正在构造,我们不在该对象的构造函数。除了其他任何东西,该对象(m_parent
)是一个指针,它没有构造函数。顺便说一下,它是未初始化的,所以你的代码完全是非法的,但不是因为你问的原因。