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?

Mag Roader
fonte
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
Adam Rosenfield
fonte
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];
    }
};
Fred Larson
fonte
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.

Rob Kennedy
fonte
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.

Johannes Schaub - litb
fonte
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.

JohnMcG
fonte
5

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

Matt Cruikshank
fonte
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

Sr.Angel
fonte
Downvoting devido a link de spam
truthadjustr