将指向部分构建对象的指针转换成指向基类的指针,是否合法?

6

也就是说,这种情况总是合法的吗?

struct Derived;
struct Base { Base(Derived*); };
struct Derived : Base { Derived() : Base(this) { } };
Base::Base(Derived *self) {
    if(static_cast<Base*>(self) != this) std::terminate();
}

int main() {
    Derived d; // is this well-defined to never call terminate?
}

在评估static_cast之时,self尚未指向一个Derived对象——该对象正在构建中。例如,如果Derived有数据成员,则它们的构造函数将不会被调用。那么,转换仍然保证是定义良好的行为,导致一个等同于Basethis指针(该指针确实指向完全构造的Base基类子对象)吗?
我认为一句标准引语可以接近回答这个问题,即[conv.ptr]/3
“... 转换的结果是指向派生类对象的基类子对象的指针......”。
但我认为目前是没有派生类对象的,那么会发生什么呢?如果确实未定义,那么对于self != static_cast<Derived*>(this)是否会有变化?
(Clang和GCC编译并运行此代码 "符合预期"。)

有2个子问题:self是否保证等于this,以及对static_cast<Base*>(self)进行解引用是否定义良好。 - spectras
在评估static_cast的时候,self还没有指向一个Derived对象。为什么呢?那么此时self指向哪里呢? - Language Lawyer
它指向未初始化的存储。在https://timsong-cpp.github.io/cppwp/n4868/basic.compound#3.sentence-8方面意味着什么?很遗憾,不可能,因为https://timsong-cpp.github.io/cppwp/n4868/basic.life#6.4。 - Language Lawyer
我猜标准并没有真正定义一个对象“存在”的含义。它只说明了对象是如何被创建的。此外,我不确定该段落的限制是否完全适用于您所问的情况,即当一个对象处于生命周期之外且未在构造中时。 - Language Lawyer
@LanguageLawyer 正在构建的对象已经超出了其生命周期。我正在询问关于这样一个对象的情况。 - HTNW
显示剩余3条评论
1个回答

2
这是正确的:[class.cdtor]/3 规定:
要将指向类 X 的对象的指针(glvalue)显式或隐式转换为指向直接或间接基类 B 的指针(引用),则必须已经开始构造 X 及其所有直接或间接基类,并且这些类的析构函数尚未完成,否则转换结果为未定义行为。...
也就是说,源类型(以及从目标类型继承的任何其他基类)必须已经开始其构造函数,并且尚未完成其析构函数。即使基类的初始化程序也算作派生构造函数的开始;标准中包含一个非常相似的示例。

1
CWG1517提案的决议指出,建设期始于基础子对象建设完成之后。另一方面,该决议修改第3段,仅适用于虚拟基类。 - Language Lawyer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接