5

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.

7
  • @juanchopanza How would that sidestep the problem? The function handleMessage still needs to modify the flag. And AFAIK constructors are allowed to modify members even on const-declared objects. Commented Jul 26, 2014 at 7:37
  • 4
    What you're doing with mutable is not undefined behavior. That's the whole point of mutable. Commented Jul 26, 2014 at 7:38
  • Sorry, I am half asleep. Yes, it is OK. That is what mutable is for. Commented Jul 26, 2014 at 7:38
  • Thanks, I suspected that this was the case but I don't have an official reference and was weary of designing a system around it. Feel free to post an answer. If you have a reference that would be great. If you know of a better way to get around the problem, that would be great too. Mutable always felt kind of like a hack to me but I guess there are legit use cases. Commented Jul 26, 2014 at 7:43
  • But note that while it is OK in the context you have laid out in your question, it could be problematic if you modify a single Message instance from different threads. Commented Jul 26, 2014 at 7:52

1 Answer 1

4

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); } 
Sign up to request clarification or add additional context in comments.

5 Comments

You don't need handleImpl, the rvalue ref overload can delegate freely to the lvalue ref one.
Thanks. I forgot about rvalue references in this context. I'm curios: What about pre C++11? Since rvalue references do not exist, are temporaries still mutable?
They are, but you can only mutate them directly in situ, as they cannot bind to a mutable reference. This is what leads to the "rvalues are const" myth that's popular. Something like std::vector<int>().push_back(5); is perfectly legal C++03.
@Puppy Thanks for the edit. One final question. What if SomeClass().someMethod() returns a lvalue reference, and that is passed into a function? Is that illegal?
Nope, it's perfectly legit.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.