Reputation: 30078
I always assumed:
const_cast
ed variable is UBSo 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
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
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