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?
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 “cv1T4
” ([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.