为什么我可以在 constexpr 函数中修改 const_cast-ed 对象?

Why can I modify const_cast-ed object in constexpr function?

我一直假设:

所以我很困惑为什么这个代码 compiles:

constexpr int fn(){
    int v = 42;
    return [v]() {
        const_cast<int&>(v)+=5;
        return v;
    }();
}
static constexpr auto val = fn();
int main() {
    return val;
}

注意:我知道没有理由不允许这样做,因为结果应该是显而易见的,我更感兴趣的是为什么允许这样做的法律解释。

这部分是真的:

there is no UB allowed in constexpr

这部分不是:

writing to const_cast-ed variable is UB

实际规则是,来自[dcl.type.cv]/4

Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.

请注意,相关部分是 对象 是否为 const - 而不是您到达它所采取的任何路径是否为 const.在这种情况下,v 不是 const 对象,也不是在复制时在 lambda 中创建的对象。

但是,如果您将 v 声明为 const,那么在 lambda 中声明的那个也将被声明为 const,因此修改它的尝试将是未定义的行为。因此,您的代码将无法编译。

lambda 表达式转换为类似于此的内容:

struct unnamed {
    int v;
    int operator()() const {
       const_cast<int&>(v)+=5;
       return v;
    }
};

没有 const_cast 你不能在 operator() 中修改 v 因为运算符是 const 方法,但 v 本身不是 const.

情况与

相同
struct foo {
    int x = 0;
    void operator() const {
        const_cast<int&>(x) += 42;
    }
};

那么这是“好的”:

foo f;
f();

虽然这是未定义的:

const foo f;
f();