Задать вопрос
@Acaunt

Почему я могу изменять состояние объекта хранящийся в const std::unique_ptr и const std::shared_ptr?

Я почему то считал, что данный код не будет работать:
#include <iostream>
#include <memory>

void foo(const std::unique_ptr<int>& ptr) {
    if (ptr) {
        *ptr += 5;
        std::cout << *ptr;
    }
}

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(5);
    foo(ptr);
}

Мне в проекте нужен был std::observer_ptr, который так и не добавили в стандарт. Я до этого считал, что приставка const делает не только указатель константным, но и при обращении к объекту его тоже константным. Но в процессе написания и сравнения работы с std::unique_ptr и std::shared_ptr моё мнение оказалось ложным.

В своем observer_ptr я сделал operator* примерно так:
template<class T>
class observer_ptr {
public:
    T& operator*() { return *ptr; }
    const T& operator*() const { return *ptr; }
    ...
};

Почему было так решено?
И как тогда делать указатель настолько константным в функциях, чтобы и объект был константным?
  • Вопрос задан
  • 21 просмотр
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Можно сделать указатель на const. Вот этот ваш const, он относится к самому указателю, его нельзя менять (в смысле, на другой адрес). Но после разыменовывания получается не константная ссылка. Вот оно в документации:
typename std::add_lvalue_reference<T>::type operator*() const


Там нет никакого const в типе возвращаемого значения, несмотря на то, что оператор можно вызывать у константных экземпляров класса. Зачем конкретно так сделано, я не знаю. Наверно, тут копируется поведение обычных указателей: там тоже можно иметь неизменный указатель на изменяемую область памяти.

Так что если вы хотите запретить менять объект, то можно сделать так:
void foo(const std::unique_ptr<const int>& ptr) {
    if (ptr) {
        *ptr += 5; // Ошибка компиляции.
        std::cout << *ptr;
    }
}

int main() {
    std::unique_ptr<const int> ptr = std::make_unique<const int>(5);
    foo(ptr);
}


Правда, придется писать много лишнего кода, если будете передавать неконстантный объект внутрь функции, которая хочет ссылку на константный.

Но вообще, обычно нет смысла передавать unique_ptr по ссылке. Можно передать по ссылке сам объект, все равно владение в функцию не передается же. И уже там можно навешивать const, если надо. Или, если передавать просто unique_ptr, без ссылки, то даже лишнего кода не надо для обработки const:
void foo(const std::unique_ptr<const int> ptr) {
}

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(5);
    foo(std::move(ptr));
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы