Потокобезопасные константные методы



Представьте, что у вас есть есть кастомный контейнер, который хранит ваши данные каким-то нетривиальным образом. Как и у любого контейнера, у него должны быть методы аля insert(…) для вставки значения в контейнер и методы для доступа к элементам get(…)(оператор [], at() и тд). Согласно правилам организации хорошего и понятного кода вы объявили get() как const. Оно и понятно, ведь любой пользователь вашего контейнера будет понимать, что доступ к элементам контейнера никак не будет отражаться на его внутреннем устройстве. Это делает код более понятным и безопасным для пользователя. Все хорошо и прекрасно.



Но тут вам приходит идея сделать этот контейнер потокобезопасным. Проблем нет, заводим shared_mutex как поле объекта и лочим операции вставки и доступа. Используем read-write lock, чтобы несколько потоков одновременно могли безопасно читать из контейнера значения, а как только придёт пишущий поток, блокировать операции чтения и записи для других потоков. Компилируем это дело и получаем ошибку. Причём гцц вам как всегда выдаст самое понятное сообщение об ошибке. Такое, что глаза вытекают и мозг плавится. Но рано или поздно осознание придёт. Вы не можете изменять поля класса в константных методах. Что делать?



На деле проблема серьёзная. С одной стороны, пользователь должен получать доступ к чтению элементов контейнера у константного объекта, иначе он по сути бесполезен. С другой стороны, существует ряд юз-кейсов, когда для оптимизации константной операции требуется изменять внутреннее состояние объекта. Классика - это использование кэш-значения и замков.



Поэтому в языке есть ключевое слово, про которое все, вплоть до мидлов, забывают и встречаются с ним только на собесах. Mutable. Этот keyword разрешает изменять какое-то поле класса в константных методах. Объявим наш shared_mutex как mutable и все заработает.



Решение элегантное, красивое и лаконичное. Только вот само использование этого приема попахивает нарушением инкапсуляции. Немножко совсем. Поэтому не стоит злоупотреблять этим приемом и скрывать им недостатки архитектуры класса. Если вам нужно поменять объект в константном методе и это не общепринятый кейс использования mutable - вам скорее всего нужно пересмотреть дизайн.



Stay safe. Stay cool.



#multitasking #cppcore #design