Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I always assumed:

  • writing to const_cast ed variable is UB
  • there is no UB allowed in constexpr

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.

like image 443
NoSenseEtAl Avatar asked Jul 09 '21 22:07

NoSenseEtAl


People also ask

Can constexpr be modified?

A template metaprogram runs at compile, but a constexpr function can run at compile time or runtime. Arguments of a template metaprogram can be types, non-types such as int , or templates. There is no state at compile time and, therefore, no modification.

Does constexpr imply const function?

constexpr implies const. A constexpr variable must satisfy the following requirements: it must be immediately constructed or assigned a value. the constructor parameters or the value to be assigned must contain only literal values, constexpr variables and functions.

What is the point of constexpr?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.


1 Answers

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.

like image 65
Barry Avatar answered Nov 10 '22 11:11

Barry