Мне бы хотелось, чтобы функция-член возвращала ссылку unique_ptr на константный тип, как в необработанных указателях.
Вот что я имею в виду:
class Foo {
private:
int _number;
public:
[[nodiscard]] explicit Foo(int number) : _number(number) {}
[[nodiscard]] int number() const { return _number; }
void set_number(int number) { _number = number; }
};
class ManageFoo {
private:
std::unique_ptr<Foo> foo_ptr;
public:
explicit ManageFoo(std::unique_ptr<Foo> &&foo_ptr) : foo_ptr(std::move(foo_ptr)) {};
void set_number(const int &number) {
foo_ptr->set_number(number);
// And perform something else...
}
const Foo *getFoo() { return foo_ptr.get(); }
};
Я хочу, чтобы пользователь ManageFoo
менял состояние Foo только через ManageFoo set_number
. Вот почему я хочу, чтобы getFoo
возвращал указатель на const Foo
. Я хочу, чтобы пользователь имел доступ только к версии Foo
, доступной только для чтения. Чтобы избежать использования foo_ptr.get()
, можно ли написать getFoo
возвращающий std::unique_ptr<const Foo>&
?
Ваша просьба кажется ошибкой. Возврат std::unique_ptr
уместен только при передаче права собственности. В вашем случае похоже, что ManageFoo
продолжает оставаться владельцем. Если ManageFoo::foo_ptr
остается действительным, он должен быть единственным существующим unique_ptr
(это означает уникальность).
Однако я мог бы рассмотреть возможность добавления const Foo* operator->() const
и const Foo& operator*() const
либо вместо getFoo()
, либо в дополнение.
Также обратите внимание, что если вам удалось получить unique_ptr<const Foo>& getFoo()
, вызывающая сторона могла бы сделать mfoo.getFoo().reset(new Foo(42))
, заменив весь объект Foo
на наш из-под ManageFoo
. Указатель на константу — это не то же самое, что константный указатель, и это справедливо и для интеллектуальных указателей.
Почему бы просто не вернуться const Foo*
?
Меня беспокоит то, что если я выставлю необработанный указатель, пользователь может попытаться удалить его где-то еще. Последнее наблюдение Фойгта интересно: мой запрос должен быть «const unique_ptr<const Foo>& getFoo()» !!
Раньше я возвращал unique_ptr
ссылки, потому что считал, что они добавляют ясности. Поговорив с другими программистами, использующими мой код, я убедился, что это мало что добавляет, кроме боли. Теперь я возвращаю необработанные указатели, когда право собственности не передается, и доверяю своим коллегам, что они не выстрелят себе в ногу необработанными указателями.
В современном C++
никто не должен удалять необработанные указатели. (если вы не пишете свой собственный умный указатель).
пользователь может попытаться удалить его где-то еще — такой пользователь даже не остановится на delete foo.get()
.
Поскольку можно вернуть «const Foo*» из «Foo*», не было бы неплохо иметь это также и в unique_pointers? Что вы думаете?
Вы говорите о свойстве const-propagation, которого в настоящее время нет ни у одного из стандартных интеллектуальных указателей. Да, это свойство может быть желательным, но оно противоречит тому факту, что вам не следует передавать или возвращать unique_ptr, за исключением намеренной передачи права собственности.
«Меня беспокоит то, что если я выставлю необработанный указатель, пользователь может попытаться удалить его где-то еще». -- Если вы откроете объект, пользователь может попытаться удалить его где-нибудь еще. Не имеет значения, возвращаете ли вы интеллектуальный указатель, необработанный указатель или ссылку; если пользователь может получить доступ к объекту напрямую, адрес доступен и может быть передан delete
. Это не твоя проблема. C++ не является безопасным языком для тех, кто хочет нарушить условности.
void set_number(const int &number) { foo_ptr->set_number(number); // And perform something else... }
foo_ptr
, кажется, никогда не становится нулевым. Тогда вы можете решить свои проблемы в комментариях, как показано ниже.
const Foo& getFoo() const { return *foo_ptr; }
const Foo *getFoo() const { return foo_ptr.get(); }
— идеальный добытчик. Какую пользу от возвращенияstd::unique_ptr<const Foo>&
вы ожидаете?