Имеет ли ключевое слово 'mutable' какие-либо другие цели, кроме разрешения изменения члена данных в константной функции-члене?
Недавно я наткнулся на код, где член данных класса был помечен ключевым словом mutable
. Насколько я понимаю, это просто позволяет модифицировать член в методе, квалифицированном как const
:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
Это единственное применение этого ключевого слова или за ним скрываются какие-то другие возможности? Я также использовал эту технику в своем классе, пометив boost::mutex
как mutable
, что позволяет const
-методам блокировать его для обеспечения потокобезопасности, но, честно говоря, это кажется мне немного некрасивым решением.
5 ответ(ов)
Это позволяет различать побитую константность и логическую константность. Логическая константность означает, что объект не изменяется таким образом, который был бы виден через публичный интерфейс, например, в вашем примере с блокировкой. Другим примером может быть класс, который вычисляет значение в первый раз, когда оно запрашивается, и кэширует результат.
Начиная с C++11, mutable
можно использовать в лямбда-выражениях, чтобы указать, что захваченные по значению объекты являются изменяемыми (по умолчанию они не изменяемы):
int x = 0;
auto f1 = [=]() mutable { x = 42; }; // OK
auto f2 = [=]() { x = 42; }; // Ошибка: захват по значению не может быть изменён в немодулированной лямбде
Ваше использование boost::mutex
именно для этого и предназначено. Другой пример использования — это кэширование внутренних результатов для ускорения доступа.
В общем, mutable
применяется к любому атрибуту класса, который не влияет на видимое состояние объекта извне.
В приведенном вами коде mutable
может оказаться неуместным, если значение done_
влияет на внешнее состояние, это зависит от того, что содержится в части ...;.
mutable
предназначен для того, чтобы пометить конкретный атрибут как изменяемый внутри const
методов. Это его единственная цель. Подумайте дважды, прежде чем использовать его, так как ваш код, вероятно, будет более чистым и читаемым, если вы измените дизайн вместо использования mutable
.
Суть в том, что mutable
используется в ситуациях, когда объект логически является константным, но на практике требует изменения. Такие случаи редки, но они существуют.
Примеры, которые приводит автор, включают кэширование и временные отладочные переменные.
Это полезно в ситуациях, когда у вас есть скрытое внутреннее состояние, такое как кэш. Например:
class HashTable
{
...
public:
string lookup(string key) const
{
if(key == lastKey)
return lastValue;
string value = lookupInternal(key);
lastKey = key;
lastValue = value;
return value;
}
private:
mutable string lastKey, lastValue;
};
Таким образом, вы можете использовать const HashTable
объект и при этом вызывать его метод lookup()
, который изменяет внутренний кэш. Использование ключевого слова mutable
позволяет изменять значения lastKey
и lastValue
даже в константном контексте, что очень удобно для реализации кэширования.
mutable
используется, когда у вас есть переменная внутри класса, которая используется только в этом классе, чтобы сигнализировать о каких-либо действиях, например, о мьютексе или блокировке. Эта переменная не изменяет поведение класса, но необходима для обеспечения потокобезопасности самого класса. Таким образом, если бы не mutable
, вы не могли бы иметь функции с квалификатором const
, потому что эта переменная должна была бы изменяться во всех функциях, доступных извне. Поэтому mutable
был введен, чтобы сделать член переменной записываемым даже в функциях с квалификатором const
.
Спецификатор mutable
информирует как компилятор, так и читателя, что изменение значения член переменной в константной функции является безопасным и ожидаемым.
В чем разница между параметрами шаблона "typename" и "class"?
Как преобразовать экземпляр std::string в нижний регистр
Как преобразовать std::string в const char* или char*
Как реализовать паттерн проектирования Singleton?
Сон на миллисекунды