Чи безпечний const_cast?


92

Я не можу знайти багато інформації про const_cast. Єдину інформацію, яку я зміг знайти (про переповнення стека), це:

const_cast<>()Використовується для додавання / видалення сопзЬ (Ness) (або летючого-ність) змінної.

Це мене нервує. Чи може використання const_castпричини несподіваної поведінки? Якщо так, то що?

Як варіант, коли це нормально використовувати const_cast?


4
Верхня відповідь випускає те, що може бути жахливо очевидним, але варто зазначити: воно стає небезпечним, лише якщо ви намагаєтесь змінити початковий constоб'єкт за допомогою constвідміченого посилання / покажчика. Якщо замість цього ви просто const_castнамагаєтеся обійти погано (або, у моєму випадку, ліниво) специфікований API, який приймає лише не constпосилання, але буде використовуватися лише в constметодах ... жодної проблеми.
underscore_d

1
@underscore_d: Більш точна версія запитання (та відповіді), яка охоплює, а саме: Чи можна відкидати const на визначений const об'єкт, якщо він фактично не модифікований?
Пітер Кордес,

Відповіді:


88

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

9
Це неправда. Стандарт С ++. §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 Будь-яка спроба ! Про вихідну змінну немає слів.
Олексій Малістов

20
@Alexey: Вихідна змінна стосується того, на що вказують або на що посилаються. Ви можете взяти посилання const на об’єкт, що не є об’єктом const, і, отже, передача його на посилання, яке можна записати, є чітко визначеною поведінкою, оскільки об’єкт, до якого йдеться, насправді не є const.
Щеня

43
@Олексій Малістов: Ні. "Об'єкт" відноситься до фактичної області зберігання, зайнятої в пам'яті (§1.7). Посилання на const на об'єкт, що не є const, не робить об'єкт const. Лише у випадку параметра посилання const ( не параметра покажчика const) компілятору дозволяється мовчки робити копію (§5.2.2 / 5); тут справа не в цьому.
Адам Розенфілд,

8
"Однак, якщо вихідна змінна насправді була const, тоді використання const_cast призведе до невизначеної поведінки" Це твердження хибне.
Легкі перегони на орбіті

7
Це не UB, щоб використовувати const_castдля видалення constз чогось, що було спочатку оголошено const. Але насправді UB намагається писати на цей об'єкт. Поки ви щойно читаєте, у вас все добре, і саме const_castпо собі не викликає UB. Це жахлива ідея, але це не по суті UB.
Jesper Juhl

35

Я можу подумати про дві ситуації, коли 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()));
}

Інший - якщо ви використовуєте старіший компілятор, який не реалізує `` змінний '', і ви хочете створити клас, який логічно є 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може спричинити невизначену поведінку, а не те, чим це корисно
Майкл Мрозек,

13
З питання: "Як варіант, коли можна використовувати const_cast?"
Фред Ларсон,

Як у "коли це не визначено"; він не шукає прикладів, коли це корисно
Майкл Мрозек,

Ми можемо дотримуватися лише букв питання. Виходячи з цього, подання ілюстративного вживання const_castє слушною відповіддю. У heпитаннях немає, оскільки питання є лише предметом.
власне поле

24

Мені важко повірити, що це єдина інформація, яку ви могли знайти про const_cast. Посилання з другого звернення Google :

Якщо ви відкинете constness об'єкта, який був явно оголошений як const, і спробуєте змінити його, результати не визначені.

Однак, якщо ви відкидаєте constness об'єкта, який не був явно оголошений як const, ви можете безпечно його змінити.


Відповідь Grrrreaat, поєднайте це з цією відповіддю, і ви отримаєте цілу картину.
bobobobo

хм щодо другого твердження у вашій відповіді, чи можу я запитати вас, як існує "const" для об'єкта, який не був прямо заявлений як const ?.
hAcKnRoCk

Існує безліч способів зробити об’єктом, що не є const, const, @Iam. Наприклад, передавати об’єкт як параметр const-reference. Або призначити його покажчику на const. Або використовувати 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;
};

Спочатку додайте сопзЬ до типу thisуказует, то ми називаємо версію Const з getT, а потім видалити сопзЬ з повертається типу, який діє так tповинно бути неконстантним ( в іншому випадку неконстантная версія getTне може мати називали). Це може бути дуже корисно, якщо у вас є велике тіло функції і ви хочете уникнути зайвого коду.


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

1
право, перший може бути static_cast, або навіть неявним_cast (про підсилення). я виправлю це за допомогою статичного приведення. подяка
Йоханнес Шауб - літб

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

10

Коротка відповідь - ні, це не безпечно.

Довга відповідь полягає в тому, що якщо ви знаєте достатньо, щоб ним користуватися, то це повинно бути безпечним.

Під час кастингу ви по суті говорите: "Я знаю те, що компілятор не знає". У випадку const_cast, ви говорите: "Хоча цей метод приймає посилання або вказівник, що не є const, я знаю, що це не змінить параметр, який я передаю".

Отже, якщо ви насправді знаєте, про що ви стверджуєте, що знаєте, використовуючи акторський склад, тоді добре використовувати його.


5

Ви знищуєте будь-який шанс на безпеку потоків, якщо ви починаєте модифікувати речі, які компілятор вважав const.


1
Що? Якщо у вас є незмінні (const) об’єкти, ви можете тривіально поділитися ними серед потоків. У той момент, коли частина вашого коду відкидає стійкість, ви втрачаєте всю свою безпеку потоків! Чому я за це не працюю? зітхання
Matt Cruikshank

8
Const, безумовно, є корисним інструментом для створення кодобезпечного потоку, але він не дає жодних гарантій (за винятком випадку констант часу компіляції). Два приклади: об'єкт const може мати змінні члени, а наявність вказівника const на об'єкт нічого не говорить про те, чи може сам об'єкт змінюватися.
Джеймс Хопкін,

Я думаю, що це хороша відповідь, тому що я не думав про почуття довіри та безпеки оптимізатора компілятора у вашому використанні цього слова const. constце довіра. const_castпорушує цю довіру :(
bobobobo

1
Щодо змінної та потокової безпеки: 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;
}

джерело: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_const_cast.htm


Проголосування проти спаму
eigenfield
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.