Ist const_cast sicher?

92

Ich kann nicht viele Informationen finden const_cast. Die einzige Information, die ich finden konnte (über Stapelüberlauf), ist:

Das const_cast<>()wird verwendet, um Konstante (oder Flüchtigkeit) einer Variablen hinzuzufügen / zu entfernen.

Das macht mich nervös. Könnte ein const_castunerwartetes Verhalten auftreten? Wenn ja, was?

Alternativ, wann ist es in Ordnung zu verwenden const_cast?

Mag Roader
quelle
4
Die oberste Antwort übersieht etwas, das schrecklich offensichtlich sein könnte, aber es lohnt sich zu erwähnen: Es wird nur unsicher, wenn Sie versuchen, ein ursprüngliches constObjekt über eine definierte constReferenz / einen definierten Zeiger zu ändern . Wenn Sie stattdessen nur const_casteine schlecht (oder in meinem Fall träge) spezifizierte API umgehen möchten, die nur eine Nichtreferenz akzeptiert, constaber nur in constMethoden verwendet wird ... überhaupt kein Problem.
underscore_d
1
@underscore_d: Eine genauere Version der Frage (und Antwort), die Folgendes abdeckt: Darf const auf einem const-definierten Objekt
Peter Cordes

Antworten:

88

const_castist nur dann sicher, wenn Sie eine Variable umwandeln, die ursprünglich nicht war const. Wenn Sie beispielsweise eine Funktion haben, die einen Parameter von a übernimmt const char *, und eine veränderbare Funktion übergeben char *, ist es sicher, const_castdass dieser Parameter an a zurückgegeben char *und geändert wird. Wenn jedoch die ursprüngliche Variable tatsächlich war const, führt die Verwendung const_castzu undefiniertem Verhalten.

void func(const char *param, size_t sz, bool modify)
{
    if(modify)
        strncpy(const_cast<char *>(param), sz, "new string");
    printf("param: %s\n", param);
}

...

char buffer[16];
const char *unmodifiable = "string constant";
func(buffer, sizeof(buffer), true);  // OK
func(unmodifiable, strlen(unmodifiable), false); // OK
func(unmodifiable, strlen(unmodifiable), true);  // UNDEFINED BEHAVIOR
Adam Rosenfield
quelle
9
Es ist nicht wahr. C ++ Standard. §7.1.​5.1/4 says Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior Jeder Versuch ! Es gibt keine Wörter über die ursprüngliche Variable.
Alexey Malistov
20
@Alexey: Bei der ursprünglichen Variablen handelt es sich um das, worauf verwiesen wird oder worauf verwiesen wird. Sie können eine const-Referenz auf ein nicht-const-Objekt nehmen. Daher ist das Umwandeln in eine beschreibbare Referenz ein genau definiertes Verhalten, da das Objekt, auf das verwiesen wird, nicht const ist.
Welpe
43
@Alexey Malistov: Nein. Ein "Objekt" bezieht sich auf den tatsächlich im Speicher belegten Speicherbereich (§1.7). Wenn Sie eine const-Referenz auf ein Nicht-const-Objekt nehmen, wird das Objekt nicht const. Nur im Fall eines const- Referenzparameters ( kein const-Zeigerparameter) darf der Compiler stillschweigend eine Kopie erstellen (§5.2.2 / 5); Dies ist hier nicht der Fall.
Adam Rosenfield
8
"Wenn die ursprüngliche Variable tatsächlich const war, führt die Verwendung von const_cast zu undefiniertem Verhalten." Diese Anweisung ist falsch.
Leichtigkeitsrennen im Orbit
7
Es ist nicht UB, const_castum constetwas zu entfernen , das ursprünglich deklariert wurde const. Aber es ist UB, tatsächlich zu versuchen, in dieses Objekt zu schreiben. Solange Sie gerade lesen, geht es Ihnen gut und das const_castan sich verursacht keine UB. Es ist eine schreckliche Idee, aber es ist nicht von Natur aus UB.
Jesper Juhl
35

Ich kann mir zwei Situationen vorstellen, in denen const_cast sicher und nützlich ist (es kann andere gültige Fälle geben).

Eine ist, wenn Sie eine const-Instanz, eine Referenz oder einen Zeiger haben und einen Zeiger oder eine Referenz an eine API übergeben möchten, die nicht const-korrekt ist, aber Sie BESTIMMT sind, ändert das Objekt nicht. Sie können den Zeiger const_cast und an die API übergeben, wobei Sie darauf vertrauen, dass er nichts wirklich ändert. Beispielsweise:

void log(char* text);   // Won't change text -- just const-incorrect

void my_func(const std::string& message)
{
    log(const_cast<char*>(&message.c_str()));
}

Die andere Möglichkeit besteht darin, einen älteren Compiler zu verwenden, der 'mutable' nicht implementiert, und eine Klasse erstellen möchten, die logisch const, aber nicht bitweise const ist. Sie können const_cast 'this' innerhalb einer const-Methode und Mitglieder Ihrer Klasse ändern.

class MyClass
{
    char cached_data[10000]; // should be mutable
    bool cache_dirty;        // should also be mutable

  public:

    char getData(int index) const
    {
        if (cache_dirty)
        {
          MyClass* thisptr = const_cast<MyClass*>(this);
          update_cache(thisptr->cached_data);
        }
        return cached_data[index];
    }
};
Fred Larson
quelle
Dies ... scheint diese Frage nicht zu beantworten. Er fragte, ob const_castundefiniertes Verhalten verursacht werden kann, nicht welche nützlichen Anwendungen es sind
Michael Mrozek
13
Aus der Frage: "Alternativ, wann ist es in Ordnung, const_cast zu verwenden?"
Fred Larson
Wie in "wann ist es nicht undefiniert"; Er sucht nicht nach Beispielen, wann es nützlich ist
Michael Mrozek
Wir können uns nur an die Buchstaben der Frage halten. Auf dieser Grundlage ist die Darstellung einer veranschaulichenden Verwendung von const_casteine gültige Antwort. Es gibt keine heFragen, da die Frage allein das Thema ist.
Truthadjustr
24

Es fällt mir schwer zu glauben, dass dies die einzigen Informationen sind, die Sie über const_cast finden können. Zitat aus dem zweiten Google-Hit :

Wenn Sie die Konstanz eines Objekts, das explizit als const deklariert wurde, wegwerfen und versuchen, es zu ändern, sind die Ergebnisse undefiniert.

Wenn Sie jedoch die Konstanz eines Objekts, das nicht explizit als const deklariert wurde, wegwerfen, können Sie es sicher ändern.

Rob Kennedy
quelle
Grrrreaat Antwort, kombinieren Sie dies mit dieser Antwort und Sie erhalten das ganze Bild.
Bobobobo
hmm. Darf ich Sie bezüglich der zweiten Aussage in Ihrer Antwort fragen, wie es eine "Konstante" für ein Objekt gibt, das überhaupt nicht explizit als Konstante deklariert wurde?
hAcKnRoCk
Es gibt viele Möglichkeiten, ein nicht-const-Objekt zu const, @Iam zu machen. Übergeben Sie das Objekt beispielsweise als const-Referenzparameter. Oder weisen Sie es einem Zeiger auf const zu. Oder verwenden const_cast. Oder rufen Sie eine const-Methode auf.
Rob Kennedy
12

Was Adam sagt. Ein weiteres Beispiel, bei dem const_cast hilfreich sein kann:

struct sample {
    T& getT() { 
        return const_cast<T&>(static_cast<const sample*>(this)->getT()); 
    }

    const T& getT() const { 
       /* possibly much code here */
       return t; 
    }

    T t;
};

Wir fügen zuerst const zu den Typpunkten hinzu, thisrufen dann die const-Version von auf getTund entfernen dann const aus dem Rückgabetyp, der gültig ist, da ter nicht const sein muss (andernfalls könnte die nicht-const-Version von getTnicht haben wurde genannt). Dies kann sehr nützlich sein, wenn Sie einen großen Funktionskörper haben und redundanten Code vermeiden möchten.

Johannes Schaub - litb
quelle
3
Ich würde lieber statische Umwandlung für das Hinzufügen von Konstanz verwenden: static_cast <const sample *> (this). Wenn ich const_cast lese, bedeutet dies, dass der Code etwas potenziell Gefährliches tut. Deshalb versuche ich, seine Verwendung nach Möglichkeit zu vermeiden.
Mfazekas
1
Richtig, der erste kann static_cast oder sogar implicit_cast (von boost) sein. Ich werde es mit statischer Besetzung beheben. danke
Johannes Schaub - litb
3
Ich gehe hin und her, ob es besser ist const_castoder nicht static_cast. const_castkann nur machen was du willst: ändere die cv-qualifiers. static_castkann "still" andere Vorgänge ausführen, die Sie nicht beabsichtigen. Die erste Besetzung ist jedoch völlig sicher und static_casttendenziell sicherer als const_cast. Ich denke, dies ist eine Situation, in der das const_castIhre Absicht besser static_castkommuniziert , aber die Sicherheit Ihrer Handlungen besser kommuniziert.
David Stone
10

Die kurze Antwort lautet nein, es ist nicht sicher.

Die lange Antwort lautet: Wenn Sie genug wissen, um es zu verwenden, sollte es sicher sein.

Beim Casting sagen Sie im Wesentlichen: "Ich weiß etwas, das der Compiler nicht weiß." Im Fall von const_cast sagen Sie: "Obwohl diese Methode eine nicht konstante Referenz oder einen Zeiger verwendet, weiß ich, dass sie den Parameter, den ich übergebe, nicht ändert."

Wenn Sie also tatsächlich wissen, was Sie bei der Verwendung der Besetzung zu wissen behaupten, ist es in Ordnung, sie zu verwenden.

JohnMcG
quelle
5

Sie zerstören jede Chance auf Thread-Sicherheit, wenn Sie anfangen, Dinge zu ändern, die der Compiler für konstant hielt.

Matt Cruikshank
quelle
1
Was? Wenn Sie unveränderliche (const) Objekte haben, können Sie diese trivial zwischen Threads teilen. In dem Moment, in dem ein Teil Ihres Codes die Konstanz aufhebt, verlieren Sie Ihre gesamte Thread-Sicherheit! Warum bin ich dafür heruntermodifiziert? Seufzer
Matt Cruikshank
8
Const ist sicherlich ein nützliches Werkzeug, um Code threadsicher zu machen, bietet jedoch keine Garantie (außer bei Konstanten zur Kompilierungszeit). Zwei Beispiele: Ein const-Objekt kann veränderbare Elemente haben, und ein const-Zeiger auf ein Objekt sagt nichts darüber aus, ob sich das Objekt selbst ändern könnte.
James Hopkin
Ich denke, dies ist eine gute Antwort, da ich bei Ihrer Verwendung des Wortes nicht an das Gefühl des Vertrauens und der Sicherheit des Compiler-Optimierers gedacht habe const. constist Vertrauen. const_castbricht dieses Vertrauen :(
Bobobobo
1
In Bezug auf veränderliche und Thread-Sicherheit: channel9.msdn.com/posts/…
MFH
-3
#include <iostream>
using namespace std;

void f(int* p) {
  cout << *p << endl;
}

int main(void) {
  const int a = 10;
  const int* b = &a;

  // Function f() expects int*, not const int*
  //   f(b);
  int* c = const_cast<int*>(b);
  f(c);

  // Lvalue is const
  //  *b = 20;

  // Undefined behavior
  //  *c = 30;

  int a1 = 40;
  const int* b1 = &a1;
  int* c1 = const_cast<int*>(b1);

  // Integer a1, the object referred to by c1, has
  // not been declared const
  *c1 = 50;

  return 0;
}

Quelle: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_const_cast.htm

Mr. Angel
quelle
Downvoting wegen Spam-Link
Truthadjustr