Const_cast est-il sûr?

92

Je ne trouve pas beaucoup d'informations sur const_cast. La seule information que j'ai pu trouver (sur Stack Overflow) est:

Le const_cast<>()est utilisé pour ajouter / supprimer const (ness) (ou volatile-ness) d'une variable.

Cela me rend nerveux. L'utilisation d'un const_castcomportement pourrait-elle provoquer un comportement inattendu? Si oui, quoi?

Sinon, quand est-il possible de l'utiliser const_cast?

Roader Mag
la source
4
La réponse principale néglige quelque chose qui pourrait être horriblement évident mais qui mérite d'être déclaré: cela ne devient dangereux que si vous essayez de modifier un constobjet d' origine via une constréférence / un pointeur dé- dé- . Si, à la place, vous ne faites que const_castcontourner une API mal spécifiée (ou, dans mon cas, paresseuse) qui n'accepte qu'une non- constréférence mais qui ne sera utilisée que dans des constméthodes ... aucun problème.
underscore_d
1
@underscore_d: Une version plus précise de la question (et de la réponse) qui couvre ce qui est: Est-il autorisé à rejeter const sur un objet défini par const tant qu'il n'est pas réellement modifié?
Peter Cordes

Réponses:

88

const_castest sûr uniquement si vous lancez une variable qui n'était pas à l'origine const. Par exemple, si vous avez une fonction qui prend un paramètre d'unconst char * et que vous transmettez un modifiable char *, vous pouvez const_castretourner ce paramètre en toute sécurité à a char *et le modifier. Cependant, si la variable d'origine était en fait const, alors l'utilisation const_castentraînera un comportement indéfini.

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
la source
9
Ce n'est pas vrai. Norme C ++. §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 N'importe quelle tentative ! Il n'y a pas de mots sur la variable d'origine.
Alexey Malistov
20
@Alexey: La variable d'origine concerne ce qui est pointé ou référencé. Vous pouvez prendre une référence const à un objet non-const, et par conséquent, le convertir en une référence inscriptible est un comportement bien défini car l'objet référencé n'est pas réellement const.
Puppy
43
@Alexey Malistov: Non. Un "objet" fait référence à la région de stockage réelle occupée en mémoire (§1.7). Prendre une référence const à un objet non const ne rend pas l'objet const. Ce n'est que dans le cas d'un paramètre de référence const ( pas un paramètre de pointeur const) que le compilateur est autorisé à faire une copie en silence (§5.2.2 / 5); Ce n'est pas le cas ici.
Adam Rosenfield
8
"Cependant, si la variable d'origine était en fait const, l'utilisation de const_cast entraînera un comportement indéfini" Cette déclaration est fausse.
Courses de légèreté en orbite le
7
Ce n'est pas UB à utiliser const_castpour supprimer constquelque chose qui a été initialement déclaré const. Mais il est UB d'essayer effectivement d'écrire à cet objet. Tant que vous venez de lire, tout va bien et const_castcela ne cause pas d'UB. C'est une idée horrible, mais ce n'est pas intrinsèquement UB.
Jesper Juhl
35

Je peux penser à deux situations où const_cast est sûr et utile (il peut y avoir d'autres cas valides).

La première est lorsque vous avez une instance, une référence ou un pointeur const, et que vous souhaitez passer un pointeur ou une référence à une API qui n'est pas constante, mais que vous êtes CERTAIN que vous ne modifiez pas l'objet. Vous pouvez const_cast le pointeur et le transmettre à l'API, en sachant que cela ne changera vraiment rien. Par exemple:

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()));
}

L'autre est si vous utilisez un compilateur plus ancien qui n'implémente pas «mutable» et que vous souhaitez créer une classe qui est logiquement const mais pas au niveau du bit. Vous pouvez const_cast 'this' dans une méthode const et modifier les membres de votre classe.

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
la source
Cela ... ne semble pas répondre à cette question. Il a demandé si cela const_castpouvait provoquer un comportement indéfini, pas quelles applications utiles de celui-ci sont
Michael Mrozek
13
De la question: "Sinon, quand est-il acceptable d'utiliser const_cast?"
Fred Larson
Comme dans "quand n'est-il pas indéfini"; il ne cherche pas d'exemples quand c'est utile
Michael Mrozek
Nous ne pouvons que nous en tenir aux lettres de la question. Sur cette base, la présentation d'une utilisation illustrative de const_castest une réponse valable. Il n'y a pas hede questions car la question seule est le sujet.
eigenfield
24

J'ai du mal à croire que ce soit la seule information que vous puissiez trouver sur const_cast. Citant le deuxième hit de Google :

Si vous rejetez la constness d'un objet qui a été explicitement déclaré comme const et que vous essayez de le modifier, les résultats ne sont pas définis.

Cependant, si vous rejetez la constness d'un objet qui n'a pas été explicitement déclaré comme const, vous pouvez le modifier en toute sécurité.

Rob Kennedy
la source
Réponse Grrrreaat, combinez cela avec cette réponse et vous obtenez une image complète.
bobobobo le
hmm. en ce qui concerne la deuxième déclaration de votre réponse, puis-je vous demander comment existe-t-il une "const" pour un objet qui n'a pas été explicitement déclaré comme const en premier lieu ?.
hAcKnRoCk
Il existe de nombreuses façons de rendre un objet non const, @Iam. Par exemple, transmettez l'objet en tant que paramètre de référence const. Ou affectez-le à un pointeur vers const. Ou utilisez const_cast. Ou appelez une méthode const dessus.
Rob Kennedy
12

Ce que dit Adam. Un autre exemple où const_cast peut être utile:

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;
};

Nous ajoutons d'abord const au type thissur getTlequel pointe, puis nous appelons la version const de , puis nous supprimons const du type de retour, qui est valide car tdoit être non-const (sinon, la version non-const de getTne pourrait pas avoir été appelé). Cela peut être très utile si vous avez un corps de fonction volumineux et que vous souhaitez éviter le code redondant.

Johannes Schaub - litb
la source
3
Je préférerais utiliser un cast statique pour l'ajout de constness: static_cast <const sample *> (this). Quand je lis const_cast, cela signifie que le code fait quelque chose de potentiellement dangereux, alors j'essaie d'éviter son utilisation lorsque cela est possible.
mfazekas
1
à droite, le premier peut être static_cast, ou même implicit_cast (de boost). Je vais le réparer en utilisant un casting statique. merci
Johannes Schaub - litb
3
Je reviens en arrière si const_castou static_castest mieux. const_castne peut faire que ce que vous voulez: changer les qualificatifs cv. static_castpeut effectuer «silencieusement» d'autres opérations que vous n'avez pas l'intention de faire. Cependant, le premier plâtre est entièrement sûr et a static_casttendance à être plus sûr que const_cast. Je pense que c'est une situation où le const_castcommunique mieux votre intention, mais le static_castcommunique mieux la sécurité de vos actions.
David Stone
10

La réponse courte est non, ce n'est pas sûr.

La réponse longue est que si vous en savez assez pour l'utiliser, alors il devrait être sûr.

Lorsque vous lancez un casting, vous dites essentiellement: "Je sais quelque chose que le compilateur ne sait pas." Dans le cas de const_cast, ce que vous dites est: "Même si cette méthode prend une référence ou un pointeur non-const, je sais que cela ne changera pas le paramètre que je lui passe."

Donc, si vous savez vraiment ce que vous prétendez savoir en utilisant le casting, alors vous pouvez l'utiliser.

JohnMcG
la source
5

Vous détruisez toute chance de sécurité des threads, si vous commencez à modifier des choses que le compilateur pensait const.

Matt Cruikshank
la source
1
Quoi? Si vous avez des objets immuables (const), vous pouvez les partager de manière simple entre les threads. À l'instant où un morceau de votre code jette la constance, vous perdez toute votre sécurité de thread! Pourquoi suis-je down-modded pour cela? soupir
Matt Cruikshank
8
Const est certainement un outil utile pour rendre le code thread-safe, mais il ne donne aucune garantie (sauf dans le cas des constantes à la compilation). Deux exemples: un objet const peut avoir des membres mutables, et avoir un pointeur const sur un objet ne dit rien sur le fait que l'objet lui-même soit en train de changer.
James Hopkin
Je pense que c'est une bonne réponse car je n'ai pas pensé aux sentiments de confiance et de sécurité de l'optimiseur du compilateur dans votre utilisation du mot const. constest la confiance. const_castbrise cette confiance :(
bobobobo
1
Concernant mutable et thread-safety: 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;
}

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

Monsieur Angel
la source
Vote négatif en raison d'un lien de spam
eigenfield