常量值的改变

11

假设我有一个结构体定义:

struct thing
{
    thing* x;
    int z;

    thing() : x(this), z(0) {}
    void foo() const
    {
        this->x->z++;
    }
};

请注意,我创建了一个可变指向自己的指针(邪恶笑声)。然后我可以像这样稍后使用它:
int main()
{
    const thing c;
    c.foo();
    assert(c.z == 1);
    c.foo();
    assert(c.z == 2);
    return c.z;
}

正如您所看到的,我似乎可以更改常量值......这是否属于未定义行为?


4
是的,这是UB(未定义行为),因为c是const。 - geza
没有一般规则说你不能在没有强制转换的情况下颠覆const。 - curiousguy
2个回答

10

[dcl.type.cv]p4:

除了任何声明为mutable([dcl.stc])的类成员可以被修改外,在其生命周期内尝试修改([expr.ass],[expr.post.incr],[expr.pre.incr])const对象([basic.type.qualifier])的任何操作都会导致未定义行为。

[basic.type.qualifier]p1:

const对象是类型为const T或此类对象的不可变子对象。

c.z是一个const对象,因为它是c的不可变子对象。你的代码在其生命周期内尝试修改它。因此,该代码具有未定义行为。


0

foo函数本身是可以的,因为像T::foo() const这样的const成员函数只是表示thisconst *T类型;那么一个(非常量)成员指向同一对象的事实就不相关了。

然而,在第一位置上的对象c被声明为常量。因此,通过任何代码(包括本来正确的成员函数foo)更改c的内容都是未定义行为。


2
“c” 是 “const”的事实怎么样? - NathanOliver
@NathanOliver:是的;您可能无法更改c->x的值,但可以更改由c->x引用的对象;因此,即使c是const,c->x->z++也是有效的。 - Stephan Lechner
我认为在C++11下这是const的违规使用。在C++11之前,这可能只是一个不好的代理。 - Eljay
1
@NathanOliver:不,我非常确定这不是未定义行为(UB)。const并不意味着对象通常是不可变的;它只是声明必须不能通过手头的(const)指针更改。因此,您无法通过(const*) this->z++更改z,但可以通过到该对象的另一条路径,即通过this->x->z++来更改它。 - Stephan Lechner
1
你的例子之所以有效,是因为 value 在第一次声明时没有被声明为 const。但他的 thing 实例被声明为 const。如果你试图使用 const_cast<thing*>(this)->z++; 来修改它,那么这将是未定义的行为。那么为什么他能够通过 this 的浅拷贝来修改它呢?*注意:(我没有投反对票)。 - Brandon
显示剩余2条评论

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