常量对象的构造

Construction of const object

本文关键字:对象 常量      更新时间:2023-10-16

C++11 §12.1/14:

在构造 const 对象期间,如果对象或其任何子对象的值是通过 不是直接或间接从构造函数的 this 获得的 指针,这样获得的对象或子对象的值为 未指定。 [示例:

struct C;
void no_opt(C*);
struct C {
    int c;
    C() : c(0) { no_opt(this); }
};
const C cobj;
void no_opt(C* cptr) {
    // value of cobj.c is unspecified
    int i = cobj.c * 100;
    cptr->c = 1;
    // value of cobj.c is unspecified
    cout << cobj.c * 100 << 'n';
}

编译上述示例输出100 .我的问题是,为什么在输入构造函数之前初始化列表将其设置为 0 时,应该未指定 cobj.c 的值?如果使用非常量对象,此行为有何不同?

编译器可以将真正const的对象视为合法常量。它可以假设它们的值永远不会改变,甚至将它们存储在const内存中,例如.ROM或闪存。因此,只要对象实际上不是恒定的,就需要使用 this 提供的 non-const 访问路径。这种情况仅在对象构建和销毁期间存在。

顺便说一句,我认为不需要对析构函数有相应的要求,因为对象生命周期已经结束,一旦cobj的析构函数开始,cobj.c就无法访问。

正如 Matthieu 所提到的,在建造或破坏过程中,除了通过this访问对象之外,这是一种强烈的"代码气味"。回顾 C++11 §3.8 [basic.life] ¶1 和 6,似乎构造函数内部的cobj.c是 UB,原因与它在析构函数内部的原因相同,无论对象const或 §12.1/14,因为它的生存期直到初始化完成(构造函数返回(才开始。

它可能有效,但它会给优秀的C++程序员敲响警钟,而且根据书本,这是非法的。

引用规则的原因是允许编译器使基于对象的恒定性的优化。 例如根据优化情况,编译器可能会替换第二个 cobj.c * 100i no_opt. 更有可能的是,在这个特定的在这种情况下,优化器将抑制i及其初始化完全,因此代码看起来可以正常工作。 但这可能不是如果你在更改cptr->c之前也输出i;这一切都取决于关于编译器优化的积极性。 但是编译器被允许假设*cptr不是cobj的别名,因为cobj是一个const 对象,其中当你通过 *cptr 进行修改时,所以它不能指向没有未定义行为的 const 对象。

当然,如果对象不是常量,则不会出现问题;编译器必须始终考虑 *cptrcobj .