2016-06-08 11 views
13

Der folgende Code ist, soweit ich es verstehe, undefiniertes Verhalten nach dem C++ - Standard (insbesondere Abschnitt 7.1.5.1.4 [dcl.type.cv]/4) .Nicht-triviales Beispiel für undefiniertes Verhalten mit const_cast

#include <iostream> 

struct F; 
F* g; 

struct F { 
    F() : val(5) 
    { 
     g = this; 
    } 
    int val; 
}; 


const F f; 

int main() { 
    g->val = 8; 
    std::cout << f.val << std::endl; 
} 

Allerdings druckt '8' mit jedem Compiler und Optimierung Einstellung habe ich ausprobiert.

Frage: Gibt es ein Beispiel, das unerwartete Ergebnisse mit dieser Art von "implizitem const_cast" zeigen wird?

Ich hoffe auf etwas so spektakulär wie die Ergebnisse der

#include <iostream> 
int main() { 
    for (int i = 0; i <=4; ++i) 
     std::cout << i * 1000000000 << std::endl; 
} 

auf, zB gcc 4.8.5 mit -O2

EDIT: den entsprechenden Abschnitt aus dem Standard-

7.1.5.1.4: Außer dass jedes deklarierte Veränderliche (7.1.1) geändert werden kann, jeder Versuch, ein konstantes Objekt während seiner Lebenszeit zu ändern (3 .8) führt zu undefiniertem Verhalten.

Als Antwort auf den Kommentar, der ein Duplikat vorschlägt; Es ist kein Duplikat, weil ich nach einem Beispiel frage, bei dem "unerwartete" Ergebnisse auftreten.

+2

Das ist eine sehr hinterhältige Einstellung - sehr nett. –

+7

Ich mag die Symptome dieses Schnipsel am unteren Rand. Null, eins, zwei, gib den Kraken frei! – Quentin

+3

Was ist der Wert beim Versuch, undefiniertes Verhalten zu definieren? – lcs

Antwort

7

Nicht so spektakulär:

f.h (Wachen weggelassen):

struct F; 
extern F* g; 

struct F { 
    F() : val(5) 
    { 
     g = this; 
    } 
    int val; 
}; 

extern const F f; 
void h(); 

TU1:

#include "f.h" 
// definitions 
F* g; 
const F f; 
void h() {}  

TU2:

#include "f.h" 
#include <iostream> 
int main() { 
    h(); // ensure that globals have been initialized 
    int val = f.val; 
    g->val = 8; 
    std::cout << (f.val == val) << '\n'; 
} 

Drucke 1 wenn mit g++ -O2 kompiliert, und 0 wenn kompiliert mit -O0.

0

Der Hauptfall von "undefined" Verhalten wäre, dass normalerweise, wenn jemand const sieht, sie davon ausgehen, dass es sich nicht ändert. So macht const_cast absichtlich etwas, das viele Bibliotheken und Programme entweder nicht erwarten würden oder das als explizites undefiniertes Verhalten betrachtet wird. Es ist wichtig, daran zu denken, dass nicht alle undefined Verhalten aus dem Standard allein stammt, auch wenn das die typische Verwendung des Begriffs ist.

Das heißt, ich war in der Lage, einen Platz in der Standard-Bibliothek zu finden, wo diese Art des Denkens angewendet werden kann, etwas, was ich zu tun glauben würde enger undefinierten Verhalten in Betracht gezogen werden: eine std::map mit „doppelten Schlüssel“ zu erzeugen:

#include "iostream" 
#include "map" 

int main() 
{ 
    std::map< int, int > aMap; 

    aMap[ 10 ] = 1; 
    aMap[ 20 ] = 2; 

    *const_cast< int* >(&aMap.find(10)->first) = 20; 

    std::cout << "Iteration:" << std::endl; 
    for(std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i) 
     std::cout << i->first << " : " << i->second << std::endl; 

    std::cout << std::endl << "Subscript Access:" << std::endl; 
    std::cout << "aMap[ 10 ]" << " : " << aMap[ 10 ] << std::endl; 
    std::cout << "aMap[ 20 ]" << " : " << aMap[ 20 ] << std::endl; 

    std::cout << std::endl << "Iteration:" << std::endl; 
    for(std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i) 
     std::cout << i->first << " : " << i->second << std::endl; 
} 

Der Ausgang ist:

Iteration: 
20 : 1 
20 : 2 

Subscript Access: 
aMap[ 10 ] : 0 
aMap[ 20 ] : 1 

Iteration: 
10 : 0 
20 : 1 
20 : 2 

mit g++.exe (Rev5, Built by MSYS2 project) 5.3.0 gebaut.

Offensichtlich gibt es eine Diskrepanz zwischen den Zugriffsschlüsseln und den Schlüsselwerten in den gespeicherten Paaren. Es scheint auch, dass das 20: 2-Paar nicht zugänglich ist, außer via Iteration.

Meine Vermutung ist, dass dies passiert, weil map als Baum implementiert ist. Wenn Sie den Wert ändern, bleibt er dort, wo er ursprünglich war (wobei 10 gehen würde), also überschreibt er nicht den anderen Schlüssel 20. Zur gleichen Zeit, eine tatsächliches 10 Hinzufügen überschreibt nicht die alten 10 weil auf den Schlüsselwert überprüft, ist es nicht eigentlich die gleichen

ich jetzt keinen Standard müssen schauen, aber ich würde erwarten, dass dies gegen die Definition von map auf ein paar Ebenen.

Es könnte auch zu schlechterem Verhalten führen, aber mit meinem Compiler/OS-Combo konnte ich es nicht dazu bringen, etwas Extremes wie Absturz zu machen.

+0

@ T.C. Ich kann keine Zitate für undefiniertes Verhalten geben, aber ich habe mich in diesem Punkt geirrt. Ich untersuchte etwas mehr und fügte das als Bearbeitung hinzu. Die wirkliche Situation ist seltsam. –

+0

Was ist * weich * undefiniertes Verhalten? – nwp

+0

Wenn Sie mit den Interna einer Klasse auf eine Art und Weise kämpfen, die ihren Vertrag verletzt und ihre Invarianten stört, wird es natürlich zusammenbrechen, egal ob 'const_cast' in diesem Prozess verwendet wird oder nicht. –

Verwandte Themen