Const_cast безопасно?


92

Я не могу найти много информации по const_cast. Единственная информация, которую я смог найти (на Stack Overflow):

const_cast<>()Используется для добавления / удаления сопзЬ (Ness) (или летучего-ность) переменной.

Это заставляет меня нервничать. Могло ли использование const_castвызвать неожиданное поведение? Если так, то?

В качестве альтернативы, когда можно использовать const_cast?


4
В верхнем ответе упускается из виду то, что может быть ужасно очевидным, но стоит отметить: это становится небезопасным только в том случае, если вы пытаетесь изменить исходный constобъект с помощью деформированной constссылки / указателя. Если вместо этого вы просто пытаетесь const_castобойти плохо (или, в моем случае, лениво) специфицированный API, который принимает только не- constссылку, но будет использоваться только в constметодах ... никаких проблем.
underscore_d

1
@underscore_d: более точная версия вопроса (и ответа), которая охватывает следующее: разрешено ли отбрасывать константу для объекта, определенного константой, если он фактически не изменен?
Питер Кордес

Ответы:


89

const_castбезопасно, только если вы приводите переменную, которая изначально не была const. Например, если у вас есть функция, которая принимает параметр a const char *, и вы передаете изменяемый char *, можно безопасно const_castвернуть этот параметр обратно в a 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

9
Неправда. Стандарт 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 Любая попытка ! Об исходной переменной нет ни слова.
Алексей Малистов 08

20
@Alexey: Исходная переменная касается того, на что указывают или на что ссылаются. Вы можете взять константную ссылку на неконстантный объект, и, следовательно, приведение ее к доступной для записи ссылке является четко определенным поведением, поскольку упомянутый объект на самом деле не является константой.
Puppy

43
@Alexey Malistov: Нет. «Объект» относится к фактической области хранения, занимаемой в памяти (§1.7). Принятие константной ссылки на неконстантный объект не делает объект константным. Только в случае константного ссылочного параметра ( не параметра константного указателя) компилятору разрешается молча сделать копию (§5.2.2 / 5); здесь дело обстоит не так.
Адам Розенфилд,

8
«Однако, если исходная переменная была на самом деле const, то использование const_cast приведет к неопределенному поведению». Это утверждение неверно.
Гонки

7
Это не UB для const_castудаления constиз того, что было заявлено изначально const. Но на самом деле попытка записи в этот объект - это UB. Пока вы просто читаете, все в порядке, и само const_castпо себе не вызывает UB. Это ужасная идея, но это не UB по своей сути.
Jesper

35

Я могу вспомнить две ситуации, когда const_cast безопасен и полезен (могут быть и другие допустимые случаи).

Один из них - когда у вас есть константный экземпляр, ссылка или указатель, и вы хотите передать указатель или ссылку на API, который не является константно-правильным, но вы НЕОБХОДИМО не будете изменять объект. Вы можете 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()));
}

Другой - если вы используете более старый компилятор, который не реализует «изменяемый», и вы хотите создать класс, который является логически константным, но не побитовым. Вы можете 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может ли оно вызывать неопределенное поведение, а не какие его полезные применения
Майкл Мрозек

13
Из вопроса: "Как вариант, когда можно использовать const_cast?"
Фред Ларсон

Например, «когда это не неопределенно»; он не ищет примеров, когда это полезно
Майкл Мрозек

Мы можем только придерживаться букв вопроса. Исходя из этого, представление иллюстративного использования const_castявляется правильным ответом. Нет никаких heвопросов, поскольку только вопрос является предметом.
eigenfield

24

Мне трудно поверить, что это единственная информация, которую вы могли найти о const_cast. Цитата из второго хита Google :

Если вы откажетесь от константности объекта, который был явно объявлен как const, и попытаетесь изменить его, результаты не будут определены.

Однако, если вы отказываетесь от константности объекта, который не был явно объявлен как const, вы можете безопасно его изменить.


Ответ Grrrreaat, объедините это с этим ответом, и вы получите полную картину.
bobobobo

хм. Что касается второго утверждения в вашем ответе, могу ли я спросить вас, как существует "константа" для объекта, который не был явно объявлен как константа с самого начала ?.
hAcKnRoCk

Есть много способов сделать неконстантный объект константным, @Iam. Например, передайте объект как параметр константной ссылки. Или назначьте его указателю на константу. Или используйте const_cast. Или вызовите для него метод const.
Роб Кеннеди

12

Что говорит Адам. Другой пример, в котором может быть полезно 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указывает, затем мы вызываем константную версию getT, а затем мы удаляем const из возвращаемого типа, который действителен, поскольку tдолжен быть неконстантным (в противном случае неконстантная версия getTне могла иметь был назван). Это может быть очень полезно, если у вас большое тело функции и вы хотите избежать избыточного кода.


3
Я бы предпочел использовать статическое приведение для добавления константности: static_cast <const sample *> (this). Когда я читаю const_cast, это означает, что код делает что-то потенциально опасное, поэтому я стараюсь по возможности избегать его использования.
mfazekas

1
правильно, первое может быть static_cast или даже implicit_cast (повышения). Я исправлю это с помощью статического приведения. спасибо
Йоханнес Шауб - лит

3
Я возвращаюсь к тому, что лучше const_castили static_castлучше. const_castможете делать только то, что вы хотите: изменять cv-квалификаторы. static_castможет «незаметно» выполнять другие операции, которые вам не нужны. Однако первое применение совершенно безопасно и, static_castкак правило, безопаснее const_cast. Я думаю, что это ситуация, когда const_castлучше передает ваши намерения, но лучше static_castпередает безопасность ваших действий.
Дэвид Стоун

10

Короткий ответ - нет, это небезопасно.

Длинный ответ таков: если вы знаете достаточно, чтобы использовать его, это должно быть безопасно.

Когда вы выполняете приведение, вы, по сути, говорите: «Я знаю то, чего не знает компилятор». В случае const_cast вы говорите: «Даже если этот метод принимает неконстантную ссылку или указатель, я знаю, что он не изменит параметр, который я ему передаю».

Так что, если вы действительно знаете, что вы утверждаете, что знаете, используя приведение, то это нормально.


4

Вы уничтожаете любую возможность обеспечения безопасности потоков, если начинаете изменять вещи, которые компилятор считал константными.


1
Какие? Если у вас есть неизменяемые (const) объекты, вы можете тривиально поделиться ими между потоками. В тот момент, когда часть вашего кода отказывается от константности, вы теряете всю свою потокобезопасность! Почему я для этого понижен? вздох
Мэтт Крукшанк

9
Const, безусловно, является полезным инструментом для обеспечения многопоточности кода, но он не дает никаких гарантий (за исключением случая констант времени компиляции). Два примера: константный объект может иметь изменяемые члены, а наличие константного указателя на объект ничего не говорит о том, может ли сам объект изменяться.
Джеймс Хопкин,

Я думаю, что это хороший ответ, потому что я не думал о чувствах доверия и безопасности оптимизатора компилятора при использовании вами этого слова const. constэто доверие. const_cast
разрушает

1
Относительно изменчивости и безопасности потоков: channel9.msdn.com/posts/…
MFH
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.