Стандарт C++ предлагает конструктор псевдонимов для shared_ptr:
template <typename T>
class shared_ptr
{
template <typename U>
shared_ptr(const shared_ptr<U>& r, element_type* ptr);
};
Такой конструктор означает, что объект shared_ptr может быть создан таким образом, что он владеет одним объектом, таким же, как у r, и в то же время сохраняет указатель на другой объект — ptr.
Кроме того, стандарт явно позволяет использовать этот конструктор для создания shared_ptr, который ничем не владеет (таким образом, соответствует определению «пустой»), но все же указывает на некоторый объект (таким образом, квалифицируемый как «ненулевой»):
[util.smartptr.shared.const]: 17. [Примечание 2: Этот конструктор позволяет создавать пустой экземпляр shared_ptr с ненулевым хранимым указателем. — примечание в конце]
Построение пустого, но ненулевого shared_ptr можно было бы легко запретить, например, потребовав, чтобы попытка создания пустого экземпляра shared_ptr с ненулевым хранимым указателем вызывала исключение.
Но стандарт решил разрешить это. Интересно, почему и для чего.
Каков предполагаемый и канонический сценарий использования пустого, но не нулевого shared_ptr?
Вероятно, это произошло случайно, когда они сделали конструктор псевдонимов. Так почему бы не разрешить?
Конструктор псевдонимов был добавлен в std::shared_ptr во время стандартизации, в N2351 «Улучшение shared_ptr для C++0x, редакция 2»:
Эта функция расширяет интерфейс shared_ptr обратно совместимым образом, что увеличивает его выразительную мощь, и поэтому настоятельно рекомендуется добавить его в стандарт C++0x. Он не вызывает проблем с совместимостью исходного кода и двоичных файлов.
В это время уже было отмечено, что:
[Примечание: этот конструктор позволяет создавать пустой экземпляр shared_ptr с сохраненным указателем, отличным от NULL. --конец примечания.]
Параллельно в boost::shared_ptr был добавлен конструктор алиасинга, который выполнял роль тестового стенда: совершить/ce72827dc73ac652ed07002b75f32e0171119c09
Что касается того, почему это разрешено: есть четкая альтернатива, которая заключается в том, чтобы пользователь создал shared_ptr из существующего указателя и неоперативного удаления:
auto sp = shared_ptr(p, [](auto){})
Однако это менее эффективно, чем псевдоним пустого состояния (поскольку должен быть выделен отдельный управляющий блок) и менее выразительно, поскольку невозможно определить, что средство удаления не работает. Так что нет причин запрещать первое.
Программист несет ответственность за то, чтобы этот ptr оставался действительным, пока существует этот shared_ptr.