Безопасен ли const_cast?
-
21-08-2019 - |
Вопрос
Я не могу найти много информации о const_cast
.Единственная информация, которую я смог найти (о переполнении стека), это:
Тот Самый
const_cast<>()
используется для добавления / удаления константы (или изменчивости) переменной.
Это заставляет меня нервничать.Мог бы использовать const_cast
вызвать неожиданное поведение?Если да, то что?
В качестве альтернативы, когда можно использовать const_cast
?
Решение
const_cast
безопасно, только если вы приводите переменную, которая изначально не была-const
.Например, если у вас есть функция, которая принимает параметр const char *
, и вы передаете изменяемый char *
, это безопасно для const_cast
этот параметр возвращается к char *
и измените его.Однако, если исходная переменная на самом деле была const
, затем используя const_cast
приведет к неопределенному поведению.
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
Другие советы
Я могу вспомнить две ситуации, когда const_cast безопасен и полезен (могут быть и другие допустимые случаи).
Один из них - когда у вас есть экземпляр const, ссылка или указатель, и вы хотите передать указатель или ссылку на API, который не является корректным с точки зрения const, но который, вы уверены, не изменит объект.Вы можете const_cast указатель и передать его API, полагая, что на самом деле это ничего не изменит.Например:
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()));
}
Другой вариант, если вы используете более старый компилятор, который не реализует 'mutable', и вы хотите создать класс, который логически является const, но не побитовым const.Вы можете const_cast 'this' внутри метода const и изменять члены вашего класса.
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];
}
};
Мне трудно поверить, что это тот самый Только информацию, которую вы могли бы найти о const_cast.Цитирую из второй хит Google:
Если вы отбросите константу объекта, который был явно объявлен как const, и попытаетесь изменить его, результаты не будут определены.
Однако, если вы отбросите константу объекта, который не был явно объявлен как const , вы сможете безопасно изменять его.
То, что говорит Адам.Другой пример, где const_cast может быть полезен:
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;
};
Сначала мы добавляем const к типу this
указывает на, тогда мы вызываем const версию getT
, а затем мы удаляем const из возвращаемого типа, который действителен с тех пор, как t
должна быть неконстантной (в противном случае неконстантная версия getT
не мог быть вызван).Это может быть очень полезно, если у вас большое тело функции и вы хотите избежать избыточного кода.
Короткий ответ - нет, это небезопасно.
Длинный ответ заключается в том, что если вы знаете достаточно, чтобы использовать его, то он должен быть безопасным.
Когда вы выполняете кастинг, вы, по сути, говорите: "Я знаю кое-что, чего не знает компилятор". В случае const_cast вы говорите следующее: "Даже если этот метод принимает неконстантную ссылку или указатель, я знаю, что он не изменит параметр, который я ему передаю".
Так что, если вы действительно знаете то, что, как вы утверждаете, знаете, используя приведение, то это нормально - использовать его.
Вы уничтожаете любой шанс на потокобезопасность, если начинаете изменять вещи, которые компилятор считал постоянными.
#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;
}