NoSenseEtAl
NoSenseEtAl

Reputation: 30078

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

I always assumed:

So I am confused why this code 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;
}

note: I know there is no reason to not allow this to work since it is obvious what the result should be, I am more interested in the legal explanation why is this allowed.

Upvotes: 5

Views: 172

Answers (2)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122830

The lambda expression translates to something similar to this:

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

Without the const_cast you cannot modify v inside the operator() because the operator is a const method, but v itself is not const.

Same situation as with

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

Then this is "ok":

foo f;
f();

While this is undefined:

const foo f;
f();

Upvotes: 4

Barry
Barry

Reputation: 303357

This part is true:

there is no UB allowed in constexpr

This part is not:

writing to const_cast-ed variable is UB

The actual rule is, from [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.

Note that the relevant part is whether the object is const or not - not whether any of the path that you took to get to it is const. And in this case, v is not a const object and nor is the one created in the lambda when you're copying it.

However, if you declare v to be const, then the one declared in the lambda would also be declared const, and thus the attempt to modify it would be undefined behavior. As a result, your code will no longer compile.

Upvotes: 6

Related Questions