Is this code UB?
struct A
{
void nonconst() {}
};
const A& a = A{};
const_cast<A&>(a).nonconst();
In other words, is the (temporary) object originally const
? I've looked through the standard but cannot find an answer so would appreciate quotations to relevant sections.
Edit: for those saying A{}
is not const
, then can you do A{}.nonconst()
?
The initialization of the reference a
is given by [dcl.init.ref]/5 (bold mine):
Otherwise, if the initializer expression
- is an rvalue (but not a bit-field)[...]
then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion ([conv.rval]) is applied.
So it means that the type prvalue expression that initialize the reference, A{}
, is adjusted to const A
.
Then [conv.rval] states:
A prvalue of type T can be converted to an xvalue of type T. This conversion initializes a temporary object ([class.temporary]) of type T.
So the type of the temporary object, bound to the reference is the same as the adjusted prvalue
type: const A
.
So the code const_cast<A&>(a).nonconst();
is undefined behavior.
The type of a temporary is whatever type you declared it with.
Unfortunately, as Oliv points out in their answer reference initialization rules transform the type to match the reference type so in this case a
actually refers to a const A
. It is basically doing
using const_A = const A;
const A& a = const_A{};
Because you can actually create constant prvalues if you ever want to stop a overload set from accepting a constant prvalue you need to have
ret_type function_name(some_type const&&) = delete;
otherwise if you have
ret_type function_name(some_type const&)
in the overload set it then the constant prvalue would bind to that if you only deleted
ret_type function_name(some_type&&)
instead. You can see this working with
struct bar{};
void foo(bar const&) { std::cout << "void foo(bar const&)\n"; }
void foo(bar&&) =delete;
using c_bar = const bar;
int main()
{
foo(c_bar{});
}
Here, void foo(bar const&)
gets called since c_bar{}
is actually const
instead of getting a deleted function error if you had used foo(bar{});
. Adding
void foo(bar const&&) = delete;
is needed to actually stop foo(c_bar{});
from compiling.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With