问题描述
在 previous post 中关于向下转换和类型安全的类似问题之后,我想知道以下示例是否会创建未定义的行为。
一个 Base 类的实例已经创建。不发生动态绑定。
但是,在 Base::interface 函数中, Base 类的实例被转换为 Derived 类的实例。
这安全吗?如果是,为什么?
请找到下面的一段代码。
#include <iostream>
template <typename Derived>
struct Base{
void interface(){
static_cast<Derived*>(this)->implementation();
}
};
struct Derived1: Base<Derived1>{
void implementation(){
std::cout << "Implementation Derived1" << std::endl;
}
};
int main(){
std::cout << std::endl;
Base<Derived1> d1;
d1.interface();
std::cout << std::endl;
}
解决方法
没有这个新的 Derived
可以指向的 Derived *
,所以它绝对不安全:转换本身具有未定义的行为,如 [expr.static.cast]§11 中所述(强调我的):
类型为“指向 cv1 B
”的纯右值,其中 B
是类类型,可以转换为类型为“指向 的指针”的纯右值cv2 D
”,其中 D
是从 B
派生的完整类,如果 cv2 与 cv 相同或更大- 资格比,cv1。 [...] 如果“指向 cv1 的指针B
”类型的纯右值指向一个 B
,它实际上是 {{ 类型的对象的子对象1}},结果指针指向 D
类型的封闭对象。 否则,行为未定义。
您可以通过限制对 D
的构造函数的访问来降低这种风险:
Base
这更好,但如果有人不小心定义了 template <typename Derived>
struct Base{
// Same as before...
protected:
Base() = default;
};
,仍然会出现同样的问题。可以通过特定的 struct Derived2 : Base<AnotherDerived> { };
声明来防止这种情况发生,但缺点是要授予对 friend
的私有成员的完全访问权限:
Base
请注意,这仍然允许 template <typename Derived>
struct Base{
// Same as before...
private:
friend Derived;
Base() = default;
};
在其成员函数中构造裸体 Derived
对象,但这就是我通常停止敲打鼹鼠的地方。