2014-07-26 3 views
5

Ich habe ein Nachrichtensystem, wo ich eine Struktur an verschiedene Funktionen übergeben. In einem kondensierten Beispiel wird eine Meldung wie folgt beschrieben:Ändert ein veränderbares Objekt auf einem konstanten deklarierten Objekt undefiniert?

struct Message { 
    bool wasHandled; 

    Message() { 
     wasHandled = false; 
    } 
}; 

Und ein Nachrichtenhandler wird wie folgt aufgerufen:

handleMessage(Message()); 

Nachrichten als const Referenzen übergeben werden. Meine Hauptmotivation dafür ist, dass ich das oben genannte Einzeiler schreiben kann. Wenn durch nicht konstante Referenz übergeben würde ich schreiben:

Message message; 
handleMessage(message); 

Der Griff Flag zeigt an, ob die Nachricht von der Funktion behandelt wurde. Die Funktion handleMessage muss daher das Flag wasHandled ändern. Eine mögliche Implementierung wäre:

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

Doch nach meinem Verständnis

handleMessage(Message()); 

entspricht: (Hinweis: Dies ist falsch, finden Sie in der akzeptierte Antwort)

const Message message; 
handleMessage(message); 

Deshalb ändere ich den Wert eines const Objekts. Das ist undefiniertes Verhalten.

Wird erklärt die Nachricht als

struct Message { 
    mutable bool wasHandled; 

    Message() { 
     wasHandled = false; 
    } 
}; 

es Verhalten definiert machen? Dies wird natürlich auch die Const Cast entfernen.

Beachten Sie, dass in diesem speziellen Beispiel das Flag wasHandle eigentlich nie gelesen wird, und wenn der Anrufer es wissen möchte, kann das Einzeiler nicht verwendet werden. In Wirklichkeit sind jedoch nicht alle Anrufer an der Flagge interessiert. Die Nachricht kann auch an andere Funktionen in handleMessage gesendet werden, die das Flag verwenden.

+0

@juanchopanza Wie würde das das Problem umgehen? Die Funktion handleMessage muss das Flag weiterhin ändern. Und AFAIK-Konstruktoren dürfen Mitglieder auch auf Const-deklarierten Objekten ändern. – rasmus

+4

Was Sie mit Mutable tun, ist kein undefiniertes Verhalten. Das ist der springende Punkt von veränderbar. –

+0

Entschuldigung, ich bin halb im Schlaf. Ja, das ist ok. Dafür ist "veränderbar". – juanchopanza

Antwort

4

Was Sie haben, ist übermäßig kompliziert. Zunächst einmal ist dein Verständnis von temporären Objekten falsch. Message() ist ein perfekt veränderbarer Wert. Es ist nur so, dass es nicht an die lvalue-Referenz binden kann, da es ein rvalue ist.

Wenn Sie wirklich wandelbar lvalues ​​und rvalues ​​gleich (und es ist fraglich, ob das nicht ein Symptom für einige andere Designprobleme) behandeln wollen, dann müssen Sie sollten nur zwei Funktions Überlastungen:

void handleMessage(Message & m) { handleImpl(m); } 
void handleMessage(Message && m) { handleMessage(m); } 
+0

Sie brauchen kein handleImpl, die Überladung rvalue ref kann frei an den lvalue ref delegieren. – Puppy

+0

@Puppy: In der Tat, guter Punkt. –

+0

Danke. Ich habe in diesem Zusammenhang rValue-Referenzen vergessen. Ich bin neugierig: Was ist mit pre C++ 11? Da rvalue-Referenzen nicht existieren, sind Provisorien immer noch veränderbar? – rasmus