Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is modifying a mutable on a const declared object undefined behavior?

I have a message system where I pass a struct to different functions. In a condensed example, a message is described like this:

struct Message {
    bool wasHandled;

    Message() {
        wasHandled = false;
    }
};

And a message handler is invoked like this:

handleMessage(Message());

Messages are passed as const references. My main motivation for that is so I can write the one-liner above. If passed by non-const reference I would have to write:

Message message;
handleMessage(message);

The handle flag indicates if the message was handled by the function. The function handleMessage thus needs to modify the wasHandled flag. One possible implementation would be:

void handleMessage(const Message& message) {
    const_cast<bool&>(message.wasHandled) = true;
    // Do stuff here.
}

However, according to my understanding,

handleMessage(Message());

is equivalent to: (Note: This is incorrect, see the accepted answer)

const Message message;
handleMessage(message);

Therefore I'm changing the value of a const object. That is undefined behavior.

Will declaring the message as

struct Message {
    mutable bool wasHandled;

    Message() {
        wasHandled = false;
    }
};

make it defined behavior? This will of course also remove the const cast.

Note that in this particular example the wasHandle flag is actually never read, and if the caller wants to know about it the one-liner cannot be used. However, in reality not all callers are interested in the flag. The message might also be dispatched to other functions inside handleMessage that does make use of the flag.

like image 384
rasmus Avatar asked Oct 01 '22 05:10

rasmus


1 Answers

What you have is overly complicated. First off, your understanding of temporary objects is wrong. Message() is a perfectly mutable value. It's just that it cannot bind to the lvalue reference, on account of being an rvalue.

If you really want to handle mutable lvalues and rvalues alike (and it's debatable whether that isn't a symptom of some other design problems), then you should simply have two function overloads:

void handleMessage(Message & m)  { handleImpl(m); }
void handleMessage(Message && m) { handleMessage(m); }
like image 181
Kerrek SB Avatar answered Oct 20 '22 19:10

Kerrek SB