O const_cast é seguro?


92

Não consigo encontrar muitas informações sobre const_cast. A única informação que consegui encontrar (no Stack Overflow) é:

O const_cast<>()é usado para adicionar / remover const (ness) (ou volatile-ness) de uma variável.

Isso me deixa nervoso. Poderia usar um const_castcomportamento inesperado de causa? Se sim, o quê?

Como alternativa, quando posso usar const_cast?


4
A primeira resposta ignora algo que pode ser terrivelmente óbvio, mas vale a pena ser declarado: só se torna inseguro se você tentar modificar um constobjeto original por meio de uma constreferência / ponteiro cancelado. Se, em vez disso, você estiver apenas const_casttentando contornar uma API mal especificada (ou, no meu caso, preguiçosamente), que apenas aceita uma não constreferência, mas será usada apenas em constmétodos ... sem problemas.
— sublinhado_d

1
@underscore_d: Uma versão mais precisa da pergunta (e resposta) que cobre isso é: É permitido lançar const em um objeto definido por const, desde que não seja realmente modificado?
— Peter Cordes

Respostas:


88

const_casté seguro apenas se você estiver lançando uma variável que originalmente não era const. Por exemplo, se você tem uma função que recebe um parâmetro de a const char *e passa um modificável char *, é seguro const_castvoltar a esse parâmetro para a char *e modificá-lo. No entanto, se a variável original era de fato const, o uso const_castresultará em um comportamento indefinido.

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

9
Não é verdade. Padrão 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 Qualquer tentativa ! Não há palavras sobre a variável original.
— Alexey Malistov

20
@Alexey: A variável original é sobre o que é apontado ou referido. Você pode tomar uma referência const para um objeto não const e, portanto, convertê-la para uma referência gravável é um comportamento bem definido, pois o objeto referido não é realmente const.
— Filhote de cachorro

43
@Alexey Malistov: Não. Um "objeto" refere-se à região real de armazenamento ocupada na memória (§1.7). Tomar uma referência const para um objeto não const não torna o objeto const. Apenas no caso de um parâmetro de referência const ( não um parâmetro de ponteiro const) o compilador tem permissão para fazer uma cópia silenciosamente (§5.2.2 / 5); este não é o caso aqui.
— Adam Rosenfield

8
"No entanto, se a variável original era de fato const, o uso de const_cast resultará em um comportamento indefinido" Esta afirmação é falsa.
— Corridas de leveza em órbita

7
É não UB usar const_castpara remover consta partir de algo que foi inicialmente declarado const. Mas é UB tentar realmente escrever nesse objeto. Contanto que você acabou de ler, você está bem e isso const_castpor si só não causa UB. É uma ideia horrível, mas não é inerentemente UB.
— Jesper Juhl

35

Posso pensar em duas situações em que const_cast é seguro e útil (pode haver outros casos válidos).

Uma é quando você tem uma instância const, referência ou ponteiro e deseja passar um ponteiro ou referência a uma API que não é const-correta, mas que você CERTAIN não modificará o objeto. Você pode const_cast o ponteiro e passá-lo para a API, confiando que isso não mudará nada. Por exemplo:

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

A outra é se você estiver usando um compilador mais antigo que não implementa 'mutável' e quiser criar uma classe que seja logicamente const, mas não bit a bit const. Você pode const_cast 'this' dentro de um método const e modificar membros de sua 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];
    }
};

Isso ... não parece responder a essa pergunta. Ele perguntou se const_castpode causar comportamento indefinido, não quais são suas aplicações úteis
— Michael Mrozek

13
Da pergunta: "Alternativamente, quando posso usar const_cast?"
— Fred Larson de

Como em "quando não é indefinido"; ele não está procurando exemplos de quando é útil
— Michael Mrozek

Só podemos nos apegar às letras da pergunta. Com base nisso, a apresentação de um uso ilustrativo de const_casté uma resposta válida. Não há heperguntas, pois só a questão é o assunto.
— truthadjustr

24

Acho difícil acreditar que essa é a única informação que você pode encontrar sobre const_cast. Citando o segundo hit do Google :

Se você descartar a constância de um objeto que foi explicitamente declarado como const e tentar modificá-lo, os resultados serão indefinidos.

No entanto, se você descartar a constância de um objeto que não foi declarado explicitamente como const, poderá modificá-lo com segurança.


Grrrreaat resposta, combine isso com esta resposta e você terá o quadro completo.
— bobobobo

Hmm. em relação à segunda afirmação de sua resposta, posso perguntar como há uma "const" para um objeto que não foi declarado explicitamente como const em primeiro lugar.
— hAcKnRoCk

Existem várias maneiras de fazer um objeto não const ser const, @Iam. Por exemplo, passe o objeto como um parâmetro de referência const. Ou atribua-o a um ponteiro para const. Ou use const_cast. Ou chame um método const nele.
— Rob Kennedy,

12

O que Adam diz. Outro exemplo onde const_cast pode ser útil:

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

Primeiro adicionamos const aos thispontos de tipo , então chamamos a versão const de getT, e então removemos const do tipo de retorno, que é válido, pois tdeve ser não-const (caso contrário, a versão não-const de getTnão poderia ter foi chamado). Isso pode ser muito útil se você tiver um corpo de função grande e quiser evitar código redundante.


3
Eu prefiro usar o cast estático para adicionar constness: static_cast <const sample *> (this). Quando estou lendo const_cast, significa que o código está fazendo algo potencialmente perigoso, então tento evitar seu uso quando possível.
— mfazekas

1
certo, o primeiro pode ser static_cast, ou mesmo ser implicit_cast (de boost). Vou consertar usando elenco estático. obrigado
— Johannes Schaub - litb

3
Eu vou e voltarei sobre se const_castou static_casté melhor. const_castsó pode fazer o que você quiser: alterar os qualificadores cv. static_castpode 'silenciosamente' realizar outras operações que você não pretende. No entanto, o primeiro gesso é totalmente seguro e static_casttende a ser mais seguro do que const_cast. Acho que essa é uma situação em que o const_castcomunica melhor sua intenção, mas static_castcomunica melhor a segurança de suas ações.
— David Stone

10

A resposta curta é não, não é seguro.

A resposta longa é que, se você souber o suficiente para usá-lo, ele deve ser seguro.

Quando você está lançando, o que essencialmente está dizendo é: "Eu sei algo que o compilador não sabe." No caso de const_cast, o que você está dizendo é: "Mesmo que este método aceite uma referência ou ponteiro não const, eu sei que ele não mudará o parâmetro que eu passar."

Portanto, se você realmente sabe o que afirma saber ao usar o gesso, não há problema em usá-lo.


5

Você está destruindo qualquer chance de thread-safety, se começar a modificar coisas que o compilador pensava que eram constantes.


1
O que? Se você tiver objetos imutáveis ​​(const), poderá compartilhá-los trivialmente entre as threads. No instante em que uma parte do seu código se desfaz da constância, você perde toda a segurança do thread! Por que estou desativado por isso? suspiro
— Matt Cruikshank

8
Const é certamente uma ferramenta útil para tornar o código thread-safe, mas não oferece garantias (exceto no caso de constantes de tempo de compilação). Dois exemplos: um objeto const pode ter membros mutáveis ​​e ter um ponteiro const para um objeto não diz nada sobre se o próprio objeto pode estar mudando.
— James Hopkin

Acho que essa é uma boa resposta porque não pensei sobre os sentimentos de confiança e segurança do otimizador do compilador em seu uso da palavra const. consté confiança. const_castestá quebrando essa confiança :(
— bobobobo,

1
Sobre mutable e 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;
}

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


Downvoting devido a link de spam
— truthadjustr
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.