Const_cast безопасно?

92

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

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

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

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

Mag Roader
источник
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не могла иметь был назван). Это может быть очень полезно, если у вас большое тело функции и вы хотите избежать избыточного кода.

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

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

JohnMcG
источник
4

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

Мэтт Крукшанк
источник
1
Какие? Если у вас есть неизменяемые (const) объекты, вы можете тривиально поделиться ими между потоками. В тот момент, когда часть вашего кода отказывается от константности, вы теряете всю свою потокобезопасность! Почему я для этого понижен? вздох
Мэтт Крукшанк
9
Const, безусловно, является полезным инструментом для обеспечения многопоточности кода, но он не дает никаких гарантий (за исключением случая констант времени компиляции). Два примера: константный объект может иметь изменяемые члены, а наличие константного указателя на объект ничего не говорит о том, может ли сам объект изменяться.
Джеймс Хопкин,
Я думаю, что это хороший ответ, потому что я не думал о чувствах доверия и безопасности оптимизатора компилятора при использовании вами этого слова const. constэто доверие. const_cast
разрушает
1
Относительно изменчивости и безопасности потоков: channel9.msdn.com/posts/…
MFH