В ответ на мой комментарий к некоторым ответ на другой вопрос кто-то предполагает, что-то вроде
void C::f() const
{
const_cast<C *>( this )->m_x = 1;
}
вызывает неопределенное поведение, так как объект const изменяется. Это правда? Если это не так, пожалуйста, процитируйте стандарт C ++ (пожалуйста, укажите, какой стандарт вы цитируете), который разрешает это.
Для чего это стоит, я всегда использовал этот подход, чтобы избежать создания переменной-члена mutable
если нужно написать один или два метода (так как использование mutable
делает его доступным для записи все методы).
Неопределенное поведение (попытка) изменить объект const (7.1.6.1/4 в C ++ 11).
Таким образом, важным вопросом является то, что является const-объектом и m_x
один? Если это так, то у вас есть UB. Если это не так, то здесь нет ничего, что указывало бы на то, что это будет UB — конечно, это может быть UB по какой-то другой причине, не указанной здесь (например, гонка данных).
Если функция f
вызывается на константном экземпляре класса C
, затем m_x
является константный объект и, следовательно, поведение не определено (7.1.6.1/5):
const C c;
c.f(); // UB
Если функция f
вызывается на неконстантном экземпляре класса C
, затем m_x
не является константным объектом, и, следовательно, поведение определяется, насколько нам известно:
C c;
const C *ptr = &c;
c->f(); // OK
Таким образом, если вы напишите эту функцию, то вы будете во власти вашего пользователя не создавать постоянный экземпляр C
и вызвать функцию на нем. Возможно, случаи C
создаются только какой-то фабрикой, и в этом случае вы сможете предотвратить это.
Если вы хотите, чтобы элемент данных был изменяемым, даже если весь объект const
тогда вы должны пометить его mutable
, Это то что mutable
для, и это дает вам определенное поведение, даже если f
вызывается на постоянном экземпляре C
,
Начиная с C ++ 11, const
функции-члены и операции над mutable
члены данных должны быть поточно-ориентированными. В противном случае вы нарушаете гарантии, предоставляемые стандартной библиотекой, когда ваш тип используется со стандартными библиотечными функциями и контейнерами.
Так что в C ++ 11 вам нужно было бы сделать m_x
атомарный тип, или же синхронизировать модификацию каким-либо другим способом, или как последнее средство документа, что, даже если оно помечено как const, функция f
не является потокобезопасным. Если вы не сделаете ничего из этого, тогда вы снова создадите для пользователя возможность написать код, который, по его разумному убеждению, должен работать, но который на самом деле имеет UB.
Есть два правила:
Вы не можете изменить объект const.
Вы не можете изменить объект с помощью константного указателя или ссылки.
Вы не нарушаете ни одно из правил, если базовый объект не является константой. Есть общее недоразумение что наличие константного указателя или константной ссылки на объект каким-то образом останавливает этот объект от изменения или изменения. Это просто недоразумение. Например:
#include <iostream>
using namespace std;
// 'const' means *you* can't change the value through that reference
// It does not mean the value cannot change
void f(const int& x, int* y)
{
cout << "x = " << x << endl;
*y = 5;
cout << "x = " << x << endl;
}
int main()
{
int x = 10;
f(x, &x);
}
Заметьте, нет бросков, ничего смешного. И все же объект, на который функция имеет постоянную ссылку, изменяется этой функцией. Это разрешено. Ваш код такой же, он просто делает это, отбрасывая константу.
Однако, если базовый объект является const, это недопустимо. Например, этот код segfaults на моей машине:
#include <iostream>
using namespace std;
const int i = 5;
void cast(const int *j)
{
*const_cast<int *>(j) = 1;
}
int main(void)
{
cout << "i = " << i << endl;
cast(&i);
cout << "i = " << i << endl;
}
См. Разделы 3.4.3 (квалификаторы CV) и 5.2.7 (исключение константности).
Без дальнейшего поиска, § 1.9 / 4 в стандарте C ++ 11 гласит:
Некоторые другие операции описаны в этом международном стандарте
как неопределенное (например, эффект попытки изменить const
объект).
И это то, что вы пытаетесь сделать здесь. Неважно, что вы отбрасываете константность (если вы этого не сделали, поведение четко определено: ваш код не сможет скомпилироваться). Вы пытаетесь изменить const
объект, так что вы столкнулись с неопределенным поведением.
Ваш код будет работать во многих случаях. Но не будет, если объект, на который вы его вызываете, действительно const
и среда выполнения решила сохранить его в постоянной памяти. Отбрасывать константу опасно, если только вы не уверены, что этот объект не был const
первоначально.