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.
    – rasmus
    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.
    – rasmus
    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); }
5
  • You don't need handleImpl, the rvalue ref overload can delegate freely to the lvalue ref one.
    – Puppy
    Commented Jul 26, 2014 at 9:05
  • 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?
    – rasmus
    Commented Jul 26, 2014 at 9:13
  • 1
    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
    Commented Jul 26, 2014 at 9:25
  • @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?
    – rasmus
    Commented Jul 26, 2014 at 9:33
  • Nope, it's perfectly legit.
    – Puppy
    Commented Jul 26, 2014 at 9:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.