Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is temporary object originally const?

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() ?

like image 1000
PoweredByRice Avatar asked Jan 23 '19 20:01

PoweredByRice


2 Answers

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.

like image 125
Oliv Avatar answered Nov 11 '22 10:11

Oliv


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.

like image 40
NathanOliver Avatar answered Nov 11 '22 12:11

NathanOliver