La risposta è: dipende da quale standard C ++ stai compilando. Tutto il codice è perfettamente strutturato in tutti gli standard ‡ ad eccezione di questa riga:
char * s = "My String";
Ora, la stringa letterale ha tipo const char[10]
e stiamo cercando di inizializzare un puntatore non const ad essa. Per tutti gli altri tipi diversi dalla char
famiglia dei letterali stringa, tale inizializzazione era sempre illegale. Per esempio:
const int arr[] = {1};
int *p = arr; // nope!
Tuttavia, in pre-C ++ 11, per i letterali stringa, c'era un'eccezione in §4.2 / 2:
Una stringa letterale (2.13.4) che non è una stringa letterale ampia può essere convertita in un valore di tipo " pointer to char "; [...]. In entrambi i casi, il risultato è un puntatore al primo elemento della matrice. Questa conversione viene presa in considerazione solo quando esiste un tipo di destinazione puntatore esplicito appropriato e non quando è necessaria la conversione da un lvalue a un rvalue. [Nota: questa conversione è obsoleta . Vedi allegato D. ]
Quindi in C ++ 03, il codice è perfettamente a posto (anche se deprecato) e ha un comportamento chiaro e prevedibile.
In C ++ 11, quel blocco non esiste: non esiste un'eccezione del genere per i valori letterali stringa convertiti char*
, quindi il codice è mal formato come l' int*
esempio che ho appena fornito. Il compilatore è obbligato a emettere una diagnostica, e idealmente in casi come questo che sono evidenti violazioni del sistema di tipo C ++, ci aspetteremmo che un buon compilatore non solo si conformasse a questo riguardo (es. Emettendo un avviso) ma fallisse a titolo definitivo.
Il codice idealmente non dovrebbe essere compilato, ma lo fa sia su gcc che su clang (presumo perché probabilmente c'è molto codice là fuori che sarebbe rotto con poco guadagno, nonostante questo buco del sistema di tipo sia deprecato per oltre un decennio). Il codice è mal formato e quindi non ha senso ragionare su quale potrebbe essere il comportamento del codice. Ma considerando questo caso specifico e la storia in cui è stato consentito in precedenza, non credo che sia un tratto irragionevole interpretare il codice risultante come se fosse un implicito const_cast
, qualcosa del tipo:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Con ciò, il resto del programma va perfettamente bene, poiché non tocchi mai s
più. Leggere un const
oggetto creato tramite un non const
puntatore è perfettamente OK. Scrivere un const
oggetto creato tramite un tale puntatore è un comportamento indefinito:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Poiché non ci sono modifiche da s
nessuna parte nel codice, il programma va bene in C ++ 03, dovrebbe non riuscire a compilarsi in C ++ 11 ma lo fa comunque - e dato che i compilatori lo consentono, non c'è ancora alcun comportamento indefinito in esso † . Considerando che i compilatori stanno ancora interpretando [erroneamente] le regole del C ++ 03, non vedo nulla che possa portare a comportamenti "imprevedibili". Scrivi a s
però e tutte le scommesse sono annullate. Sia in C ++ 03 che in C ++ 11.
† Anche se, ancora una volta, per definizione un codice mal formato non offre alcuna aspettativa di un comportamento ragionevole
‡ Tranne che no, vedere la risposta di Matt McNabb