Фундаментальная проблема здесь заключается в том, что scoped_ptr
объекты ведут себя больше как указатели, а не как объекты классов (даже если scoped_ptr
экземпляры фактически являются объектами класса).
Классы интеллектуальных указателей, предоставляемые Boost, предназначены для максимально возможного сохранения семантики необработанных указателей † при одновременном предоставлении дополнительных функций, таких как подсчет ссылок или (в данном случае) семантика RAII.
С этой целью operator*()
и operator->()
члены scoped_ptr
записываются так, что его "поведение константности" по существу совпадает с таковым у необработанного указателя.
Рассмотрим эту ситуацию с «тупыми» указателями:
// Can change either Foo or ptr.
Foo* ptr;
// Can't change Foo via ptr, although ptr can be changed.
const Foo* ptr;
// Can't change ptr, although Foo can be changed via ptr.
Foo* const ptr;
// Can't change Foo or ptr.
const Foo* const ptr;
Аналоги scoped_ptr
будут выглядеть так:
// Can change either Foo or ptr.
scoped_ptr<Foo> ptr;
// Can't change Foo via ptr, although ptr can be changed.
scoped_ptr<const Foo> ptr;
// Can't change ptr, although Foo can be changed via ptr.
const scoped_ptr<Foo> ptr;
// Can't change Foo or ptr.
const scoped_ptr<const Foo> ptr;
Способ написания операторов делает возможным приведенный выше фрагмент кода, даже если scoped_ptr
на самом деле не является необработанным указателем.
Во всех случаях код должен иметь возможность разыменования ptr
. Делая операторы const
, операторы разыменования / доступа к членам можно вызывать как для const
, так и для не const
scoped_ptr
с.
Обратите внимание, что если пользователь объявит scoped_ptr<Foo>
, он будет иметь следующие члены:
Foo& operator*() const;
Foo* operator->() const;
в то время как scoped_ptr<const Foo>
будет иметь следующие члены:
const Foo& operator*() const;
const Foo* operator->() const;
Таким образом, поведение указателей на const -корректность фактически сохраняется таким образом.
† Но не более того, иначе они не были бы умными указателями!