La réponse est: cela dépend de la norme C ++ avec laquelle vous compilez. Tout le code est parfaitement bien formé dans toutes les normes ‡ à l'exception de cette ligne:
char * s = "My String";
Maintenant, le littéral de chaîne a un type const char[10]
et nous essayons d'initialiser un pointeur non const vers lui. Pour tous les autres types autres que la char
famille des chaînes littérales, une telle initialisation était toujours illégale. Par exemple:
const int arr[] = {1};
int *p = arr; // nope!
Cependant, dans le pré-C ++ 11, pour les littéraux de chaîne, il y avait une exception dans §4.2 / 2:
Un littéral de chaîne (2.13.4) qui n'est pas un littéral de chaîne large peut être converti en une rvalue de type « pointer to char »; [...]. Dans les deux cas, le résultat est un pointeur vers le premier élément du tableau. Cette conversion n'est prise en compte que lorsqu'il existe un type de cible de pointeur explicite approprié, et non lorsqu'il existe un besoin général de convertir une valeur l en une valeur r. [Remarque: cette conversion est obsolète . Voir l'annexe D. ]
Ainsi, en C ++ 03, le code est parfaitement correct (bien que déconseillé) et présente un comportement clair et prévisible.
En C ++ 11, ce bloc n'existe pas - il n'y a pas d'exception pour les chaînes littérales converties en char*
, et donc le code est tout aussi mal formé que l' int*
exemple que je viens de fournir. Le compilateur est obligé d'émettre un diagnostic, et idéalement dans des cas comme celui-ci qui sont des violations manifestes du système de type C ++, nous nous attendrions à ce qu'un bon compilateur ne soit pas seulement conforme à cet égard (par exemple en émettant un avertissement) mais qu'il échoue carrément.
Le code ne devrait idéalement pas compiler - mais le fait à la fois sur gcc et clang (je suppose qu'il y a probablement beaucoup de code là-bas qui serait cassé avec peu de gain, bien que ce type de trou système soit obsolète depuis plus d'une décennie). Le code est mal formé et il est donc illogique de raisonner sur ce que pourrait être le comportement du code. Mais compte tenu de ce cas spécifique et de l'historique de son autorisation, je ne pense pas qu'il soit déraisonnable d'interpréter le code résultant comme s'il s'agissait d'un implicite const_cast
, quelque chose comme:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Avec cela, le reste du programme est parfaitement bien, car vous ne touchez s
plus jamais . La lecture d' un const
objet créé via un non- const
pointeur est parfaitement OK. L'écriture d' un const
objet créé via un tel pointeur est un comportement indéfini:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Comme il n'y a aucune modification via s
n'importe où dans votre code, le programme fonctionne bien en C ++ 03, devrait échouer à compiler en C ++ 11 mais le fait quand même - et étant donné que les compilateurs le permettent, il n'y a toujours pas de comportement indéfini † . En admettant que les compilateurs interprètent toujours [incorrectement] les règles de C ++ 03, je ne vois rien qui conduirait à un comportement "imprévisible". Écrivez à s
cependant, et tous les paris sont ouverts. Dans C ++ 03 et C ++ 11.
† Bien que, encore une fois, par définition, un code mal formé ne donne aucune attente d'un comportement raisonnable
‡ Sauf non, voir la réponse de Matt McNabb