Search code examples
c++language-lawyer

is this Undefined Behavior? (casting away const from a temporary binding to const reference)


is the following code undefined

auto main() -> int {
    const_cast<int&>(static_cast<int const&>(42)) = 21; return 0;
}

I am assigning a value to a temporary that was bound to an lvalue that is const then assigned the meaning of life to it.

Do I need this at all? Nope just curiosity kills me. Is this UB?


Solution

  • It's UB. We can see this by attempting to do it in a constant expression, which causes a compile-time error:

    constexpr int foo() {
        const_cast<int&>(static_cast<int const&>(42)) = 21; return 0;
    }
    static_assert(foo() == 0);
    

    Clang, EDG, and GCC all give an error message saying that foo() is not a constant expression because it involves modifying a const object. (I can't test MSVC right now because Godbolt doesn't have it right now.)

    The specific reason why the temporary object is const in this case is that this reference initialization is governed by [dcl.init.ref]/5.3:

    A reference to type "cv1 T1" is initialized [...] as follows:

    • [...]
    • [...] If the converted initializer is a prvalue, let its type be denoted by T4; the temporary materialization conversion ([conv.rval]) is applied, considering the type of the prvalue to be “cv1 T4” ([conv.qual]). In any case, the reference binds to the resulting glvalue (or to an appropriate base class subobject).

    Here, cv1 is const because we are initializing a const int&, so the type of the prvalue 42 is treated as const int when materializing it. This special wording creates a unique situation that is exempt from the usual rule [expr.type]/2 that prevents prvalues from having cv-qualified scalar types.

    Note that this wording is fairly recent; see CWG2657.