Ответ 1
В контролируемых обстоятельствах вы можете передать общий указатель по постоянной ссылке. Убедитесь, что никто не удаляет объект одновременно, хотя это не должно быть слишком сложно, если вы внимательно относитесь к тому, кому вы даете ссылки.
В общем, вы должны передать общий указатель как прямую копию. Это дает ему намеченную семантику: каждая область, содержащая копию общего указателя, сохраняет объект живым в силу его "доли" в собственности.
Единственная причина, по которой не всегда переходить по значению, заключается в том, что копирование общего указателя происходит с определенной ценой за счет обновления количества ссылок на атомы; однако это может не быть серьезной проблемой.
Дополнительное отступление:
Поскольку на главный вопрос был дан ответ, возможно, поучительно рассмотреть несколько способов использования никогда общего указателя. Вот небольшой мысленный эксперимент. Определим общий тип указателя SF = std::shared_ptr<Foo>
. Чтобы рассматривать ссылки, а не передавать аргументы функции, давайте посмотрим на тип RSF = std::reference_wrapper<T>
. То есть, если у нас есть общий указатель SF p(std::make_shared<Foo>());
, то мы можем создать ссылочную оболочку со значениями семантики через RSF w = std::ref(p);
. Так много для настройки.
Теперь все знают, что контейнеры указателей - это минное поле. Таким образом, std::vector<Foo*>
станет кошмаром для поддержания, и любое количество ошибок возникает из-за ненадлежащего управления жизненным циклом. Хуже концептуально то, что никогда не ясно, кому принадлежат объекты, указатели которых хранятся в контейнере. Указатели могут представлять собой сочетание указателей на динамические объекты, автоматические объекты и мусор. Никто не может сказать. Поэтому стандартным решением является использование std::vector<SF>
. Это правильный способ использования общего указателя. С другой стороны, то, что вы никогда не должны использовать, - это std::vector<RSF>
- это неуправляемый монстр, который на самом деле очень похож на оригинальный вектор голых указателей! Например, неясно, жив ли объект, к которому вы держите ссылку. Взятие ссылки на общий указатель нанесло поражение всей цели.
Для второго примера предположим, что у нас есть общий указатель SF p
, как и раньше. Теперь у нас есть функция int foo(SF)
, которую мы хотим запустить одновременно. Обычный std::thread(foo, p)
работает просто отлично, поскольку конструктор потока создает копию своих аргументов. Однако, если бы мы сказали std::thread(foo, std::ref(p))
, у нас были бы всевозможные проблемы: общий указатель в области вызова может истечь и уничтожить объект, и вы останетесь с оборванной ссылкой и недопустимым указателем!
Я надеюсь, что эти два, по общему признанию, довольно надуманные примеры проливают немного света, когда вы действительно хотите, чтобы ваши общие указатели были переданы копией. В хорошо продуманной программе всегда должно быть понятно, кто несет ответственность за какие ресурсы, а при правильном использовании общий указатель - отличный инструмент для работы.