Czy const_cast jest bezpieczne?


92

Nie mogę znaleźć zbyt wielu informacji const_cast. Jedyne informacje, które udało mi się znaleźć (na temat przepełnienia stosu) to:

const_cast<>()Jest stosowany do dodawania / usuwania const (Ness) (lub lotny-ności) o zmiennej.

To mnie denerwuje. Czy użycie jakiegoś const_castpowodu może spowodować nieoczekiwane zachowanie? Jeśli tak to co?

Ewentualnie, kiedy można go używać const_cast?


4
Najlepsza odpowiedź pomija coś, co może być okropnie oczywiste, ale jest warte stwierdzenia: staje się niebezpieczne tylko wtedy, gdy spróbujesz zmodyfikować pierwotny constobiekt za pomocą constusuniętego odniesienia / wskaźnika. Jeśli zamiast tego po prostu const_castobejdziesz słabo (lub, w moim przypadku, leniwie) interfejs API, który akceptuje tylko brak constodniesienia, ale będzie używany tylko w constmetodach ... nie ma żadnego problemu.
podkreślenie_d

1
@underscore_d: Bardziej precyzyjna wersja pytania (i odpowiedzi), która obejmuje to: Czy można
Peter Cordes,

Odpowiedzi:


88

const_castjest bezpieczny tylko wtedy, gdy rzutujesz zmienną, która pierwotnie była inna niż const. Na przykład, jeśli masz funkcję, która przyjmuje parametr a const char *i przekazujesz modyfikowalną wartość char *, możesz bezpiecznie const_castprzywrócić ten parametr z powrotem do a char *i zmodyfikować go. Jeśli jednak oryginalna zmienna była w rzeczywistości const, użycie const_castspowoduje niezdefiniowane zachowanie.

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
To nie prawda. Standard 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 Każda próba ! Nie ma słów o oryginalnej zmiennej.
Alexey Malistov

20
@Alexey: Oryginalna zmienna dotyczy tego, co jest wskazywane lub do którego się odnosi. Możesz wziąć odwołanie do stałej do obiektu innego niż const, a zatem rzutowanie go na odwołanie do zapisu jest dobrze zdefiniowanym zachowaniem, ponieważ obiekt, do którego się odnosi, nie jest w rzeczywistości stałym.
Szczenię

43
@Alexey Malistov: Nie. „Obiekt” odnosi się do rzeczywistego obszaru pamięci zajmowanego w pamięci (§1.7). Przyjęcie stałej referencji do obiektu innego niż const nie powoduje, że obiekt jest stały. Tylko w przypadku parametru odniesienia const ( nie parametru wskaźnika const) kompilator może po cichu wykonać kopię (§5.2.2 / 5); tak nie jest w tym przypadku.
Adam Rosenfield,

8
„Jeśli jednak oryginalna zmienna była faktycznie stała, to użycie const_cast spowoduje niezdefiniowane zachowanie”. To stwierdzenie jest fałszywe.
Wyścigi lekkości na orbicie

7
Nie należy używać UB do const_castusuwania constczegoś, co zostało pierwotnie zadeklarowane const. Ale to jest UB rzeczywiście spróbować napisać do tego obiektu. Dopóki tylko czytasz, wszystko jest w porządku i const_castsamo w sobie nie powoduje UB. To okropny pomysł, ale nie jest z natury UB.
Jesper Juhl

35

Przychodzą mi do głowy dwie sytuacje, w których const_cast jest bezpieczne i przydatne (mogą istnieć inne ważne przypadki).

Jeden z nich to sytuacja, w której masz instancję stałej, odwołanie lub wskaźnik i chcesz przekazać wskaźnik lub odwołanie do interfejsu API, który nie jest zgodny ze stałą stałą, ale jesteś PEWNY, że nie zmodyfikujesz obiektu. Możesz const_cast wskaźnik i przekazać go do API, ufając, że tak naprawdę niczego nie zmieni. Na przykład:

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

Druga to sytuacja, gdy używasz starszego kompilatora, który nie implementuje opcji „mutable” i chcesz utworzyć klasę, która jest logicznie stała, ale nie bitowa. Możesz const_cast „this” w ramach metody const i modyfikować członków swojej klasy.

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

To ... nie wydaje się odpowiadać na to pytanie. Zapytał, czy const_castmoże powodować nieokreślone zachowanie, a nie jakie są jego przydatne zastosowania
Michał Mrozek

13
Z pytania: „Alternatywnie, kiedy można używać const_cast?”
Fred Larson

Jak w przypadku „kiedy nie jest nieokreślone”; nie szuka przykładów, kiedy jest to przydatne
Michał Mrozek

Możemy tylko trzymać się liter pytania. W związku z tym przedstawienie przykładowego użycia const_castjest prawidłową odpowiedzią. Nie ma hepytań, ponieważ samo pytanie jest tematem.
eigenfield

24

Trudno mi uwierzyć, że to jedyna informacja, jaką można znaleźć o const_cast. Cytując z drugiego hitu Google :

Jeśli odrzucisz stałość obiektu, który został jawnie zadeklarowany jako stała, i spróbujesz go zmodyfikować, wyniki będą niezdefiniowane.

Jeśli jednak odrzucisz stałość obiektu, który nie został jawnie zadeklarowany jako const, możesz go bezpiecznie zmodyfikować.


Odpowiedź Grrrreaat, połącz ją z tą odpowiedzią, a otrzymasz pełny obraz.
bobobobo

hmm. Jeśli chodzi o drugie stwierdzenie w Twojej odpowiedzi, czy mogę zapytać, w jaki sposób istnieje „stała” dla obiektu, który nie został wyraźnie zadeklarowany jako stała w pierwszej kolejności?
hAcKnRoCk

Istnieje wiele sposobów, aby obiekt niebędący stałym obiektem był stałym, @Iam. Na przykład przekaż obiekt jako parametr odniesienia do stałej. Lub przypisz go do wskaźnika do stałej. Lub użyj const_cast. Lub wywołaj na nim metodę const.
Rob Kennedy,

12

Co mówi Adam. Inny przykład, w którym const_cast może być pomocne:

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

Najpierw dodajemy const do typu, na który thiswskazuje typ , następnie wywołujemy wersję const getT, a następnie usuwamy const z typu zwracanego, który jest prawidłowy, ponieważ tmusi być inny niż const (w przeciwnym razie wersja non-const getTnie mogłaby został wezwany). Może to być bardzo przydatne, jeśli masz dużą treść funkcji i chcesz uniknąć nadmiarowego kodu.


3
Wolałbym raczej użyć statycznego rzutowania do dodawania constness: static_cast <const sample *> (this). Kiedy czytam const_cast, oznacza to, że kod robi coś potencjalnie niebezpiecznego, więc staram się go unikać, gdy jest to możliwe.
mfazekas

1
tak, pierwszy może być static_cast lub nawet implicit_cast (z boost). naprawię to za pomocą odlewu statycznego. dzięki
Johannes Schaub - litb

3
Wracam do przodu i zastanawiam się, const_castczy static_castjest lepiej. const_castmożesz robić tylko to, co chcesz: zmienić kwalifikatory CV. static_castmoże „po cichu” wykonywać inne operacje, których nie zamierzasz. Jednak pierwsza obsada jest całkowicie bezpieczna i static_castzwykle jest bezpieczniejsza niż const_cast. Myślę, że jest to sytuacja, w której const_castlepiej komunikuje twoje zamiary, ale lepiej static_castkomunikuje bezpieczeństwo twoich działań.
David Stone

10

Krótka odpowiedź brzmi: nie, to nie jest bezpieczne.

Długa odpowiedź jest taka, że ​​jeśli wiesz wystarczająco dużo, aby go używać, powinno być bezpieczne.

Podczas przesyłania zasadniczo mówisz: „Wiem coś, czego kompilator nie wie”. W przypadku const_cast mówisz: „Nawet jeśli ta metoda przyjmuje odniesienie lub wskaźnik inne niż const, wiem, że nie zmieni parametru, który ją przekazuję”.

Więc jeśli naprawdę wiesz, co twierdzisz, że wiesz, używając obsady, możesz jej użyć.


5

Niszczysz wszelkie szanse na bezpieczeństwo wątków, jeśli zaczniesz modyfikować rzeczy, które kompilator uważał za const.


1
Co? Jeśli masz niezmienne (const) obiektów, można trywialnie dzielić się nimi między wątkami. W chwili, gdy fragment twojego kodu odrzuca konsternację, tracisz całe bezpieczeństwo wątków! Dlaczego zostałem z tego powodu obniżony? westchnienie
Matt Cruikshank,

8
Const jest z pewnością przydatnym narzędziem do zapewniania bezpieczeństwa wątkowego kodu, ale nie daje żadnych gwarancji (z wyjątkiem przypadku stałych czasu kompilacji). Dwa przykłady: obiekt const może mieć zmienne składowe, a posiadanie wskaźnika stałej do obiektu nie mówi nic o tym, czy sam obiekt może się zmieniać.
James Hopkin,

Myślę, że to dobra odpowiedź, ponieważ nie pomyślałem o poczuciu zaufania i bezpieczeństwa optymalizatora kompilatora w używaniu tego słowa const. constto zaufanie. const_castłamie to zaufanie :(
bobobobo

1
Odnośnie mutowalności i bezpieczeństwa wątków
MFH

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

źródło: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_const_cast.htm


Głosowanie przeciw z powodu odsyłacza spamerskiego
eigenfield
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.