Безопасно ли удалить константу через const_cast и вызвать неконстантную функцию, которая не изменяет полученный объект?

Я знаю, что отбрасывание const-сущности должно выполняться с осторожностью, и любая попытка удалить const-сущность из первоначально const объекта с последующим изменением объекта приводит к неопределенному поведению. Что, если мы хотим удалить const-ness, чтобы мы могли вызывать неконстантную функцию, которая не изменяет объект? Я знаю, что мы действительно должны отметить такую ​​функцию const, но предположим, что я использую «плохой» код, для которого недоступна версия const.

Итак, подведем итог, является ли приведенный ниже код "безопасным"? Я предполагаю, что до тех пор, пока вы не измените объект, все в порядке, но я не уверен на 100%.

#include <iostream>

struct Foo
{
    void f() // doesn't modify the instance, although is not marked const
    {
        std::cout << "Foo::f()" << std::endl;
    }
};

int main()
{
    const Foo foo;
    const_cast<Foo&>(foo).f(); // is this safe?
}

person vsoftco    schedule 26.04.2015    source источник
comment
Несмотря на то, что это разрешено стандартом, как уже было сказано, я бы сказал, что это не безопасно и не нормально.   -  person    schedule 27.04.2015
comment
@hvd Почему операция только для чтения может быть небезопасной?   -  person Barry    schedule 27.04.2015
comment
@Barry Дело не в этом. Эту функцию-член можно изменить позже, чтобы случайно вызвать UB.   -  person Columbo    schedule 27.04.2015
comment
@hvd Я видел такой код в некоторой реализации прокси-класса, где ctor принимал неконстантный параметр. Использование пришло в функции, которая возвращала константный прокси, но должна была вызвать неконстантный ctor для создания прокси. Довольно запутанный, но тем не менее реальный код. И да, я знаю, что не стоит писать такой код, мне просто стало любопытно, если технически это нормально.   -  person vsoftco    schedule 27.04.2015
comment
@Columbo Хороший вопрос.   -  person Barry    schedule 27.04.2015


Ответы (3)


Неопределенное поведение по отношению к const_cast определяется стандартом C ++ 11 §3.8 / 9 (§3.8 - время жизни объекта):

Создание нового объекта в том месте хранения, которое занимает объект const со статической, потоковой или автоматической продолжительностью хранения, или в месте хранения, которое такой const объект занимал до окончания срока его существования, приводит к неопределенному поведение.

и §7.1.6.1 / 4 (§7.1.6.1 - это cv-квалификаторы)

За исключением того, что любой член класса, объявленный mutable (7.1.1), может быть изменен, любая попытка изменить объект const в течение его времени жизни (3.8) приводит к неопределенному поведению.

Другими словами, вы получите UB, если измените объект изначально const, а в противном случае 1 - нет.

Сам const_cast не вводит UB.


Кроме того, в §5.2.11 / 7 есть ненормативное примечание, что в зависимости от типа запись через указатель или ссылку, полученную от const_cast, может иметь неопределенное поведение.

Это ненормативное примечание настолько сложное, что в нем есть своя ненормативная сноска, объясняющая, что const_cast не ограничивается преобразованиями, которые отбрасывают const-квалификатор.

Однако, по-прежнему с этим разъяснением, я не могу придумать ни одного случая, когда запись могла бы быть четко определенной или не зависеть от типа, то есть я не могу понять это примечание. Два других ответа здесь сосредоточены на слове написать в этой заметке, и это необходимо, чтобы попасть в UB-land через §3.8 / 9, да. Для меня довольно подозрительный аспект - это зависимость от шрифта, который, по-видимому, составляет значительную часть этой ноты.


1) За исключением случаев, когда в игру вступают правила UB, касающиеся других вещей, не связанных с const_cast, например обнуление указателя, который позже разыменован в контексте, отличном от typeid-expression.

person Cheers and hth. - Alf    schedule 26.04.2015
comment
Также есть: 7.1.6.1 За исключением того, что любой член класса, объявленный изменяемым (7.1.1), может быть изменен, любая попытка изменить константный объект во время его жизни (3.8) приводит к неопределенному поведению. - person CB Bailey; 27.04.2015
comment
@CharlesBailey: Спасибо, я добавлю это. Сделанный. - person Cheers and hth. - Alf; 27.04.2015
comment
В зависимости от типа имеет смысл, если он относится к типу, используемому при определении объекта. Квалификатор const, используемый в определении, является частью типа, и то, разрешены ли модификации, зависит от того, использовался ли этот квалификатор. - person ; 27.04.2015
comment
@hvd: Спасибо, в этом есть смысл. Какой-то извращенный смысл, поскольку они могли просто написать это. Тем не менее, я думаю, что это вероятное объяснение. :) - person Cheers and hth. - Alf; 27.04.2015

Этот конкретный пример оказался безопасным (с четко определенным поведением), потому что нет записи для объекта, который объявлен const.

person CB Bailey    schedule 26.04.2015
comment
так что пока нет записи, это безопасно, верно? Это то, о чем я думал. - person vsoftco; 26.04.2015
comment
Собственно, запись происходит, когда записываются байты, образующие представление объекта. - person CB Bailey; 27.04.2015

У нас есть это в [dcl.type.cv]:

За исключением того, что любой член класса, объявленный mutable (7.1.1), может быть изменен, любая попытка изменить объект const в течение его времени жизни (3.8) приводит к неопределенному поведению.

И есть примечание (ненормативное) в [expr.const.cast], в котором говорится:

[Примечание: в зависимости от типа объекта, операция записи через указатель, lvalue или указатель на член данных, полученная в результате const_cast, отбрасывающего const -qualifier может привести к неопределенному поведению (7.1.6.1). - конец примечания]

Попытка изменить объект или операция записи после const_cast [может] привести к неопределенному поведению. Здесь у нас нет операции записи.

person Barry    schedule 26.04.2015