Я пытаюсь написать свою собственную реализацию unique_ptr на C++, и вот мой заголовочный файл UniquePtr:
#include <iostream>
template <typename T>
class UniquePtr
{
T* ptr;
public:
UniquePtr(T* _ptr);
UniquePtr(const UniquePtr& other) = delete;
UniquePtr& operator=(const UniquePtr& other) = delete;
UniquePtr(UniquePtr&& dying) noexcept;
UniquePtr& operator=(UniquePtr&& dying) noexcept;
T& operator*();
const T& operator*() const;
/*??? operator->()
{
}*/
/*??? operator->() const
{
}*/
~UniquePtr();
};
Я не понимаю, какой оператор-> должен возвращать. Я знаю, что вызов в форме uniquePtr->member преобразуется в (*uniquePtr).member, но это все равно не помогает мне понять, что должен возвращать оператор->.
Я понимаю, что операторы — это, по сути, функции со специальным именем, поэтому мы можем думать о -> как о функции, которая принимает только один аргумент — указатель на объект (который получают все функции-члены). Однако меня немного смущает то, как перевод вызова uniquePtr->member должен диктовать способ его переопределения в классе (каким должен быть тип возвращаемого значения).
Может ли кто-нибудь объяснить, как реализовать оператор -> для этого специального класса UniquePtr и почему он реализован именно таким образом?
Это не ответ на мой вопрос:
@WeijunZhou, перестань зацикливаться на себе! Даже пользователь, ответивший на мой вопрос, сказал, что предоставленный вариант, который якобы отвечает на него, не очень хорошо объясняет оператор ->. Зная, что я могу найти ответ на СВОЙ вопрос, хотя и сказал, что не могу. Оставайтесь на своей полосе, пожалуйста.
Я согласен, что тон не идеален, и прошу прощения, если вас обидел. Мне следовало прокомментировать так: «Я верю в предложенный обман, и ссылка там отвечает на ваш вопрос, можете ли вы отредактировать свой вопрос, чтобы объяснить, почему это не так, вместо того, чтобы просто заявить, что это не так? Это может помочь получить ваш вопрос снова открыт». Кстати, я не голосовал за закрытие вашего вопроса.
Я знаю, что вызов в форме uniquePtr->member переводится в (*uniquePtr).member
Так ведет себя встроенный оператор ->
. Если вместо этого выбрана перегрузка, компилятор преобразует
uniquePtr->member
к
(uniquePtr.operator->())->member
и это будет сделано рекурсивно для нового оператора ->
.
Вы хотите, чтобы это привело к (uniquePtr.ptr)->member
, который после расширения встроенного ->
будет (*(uniquePtr.ptr)).member
. Итак, вы хотите, чтобы ваш operator->()
просто вернулся ptr
. Тип возвращаемого значения должен соответствовать const
-квалификации перегрузки.
Также обратите внимание, что поведение ->
при выборе перегрузки оператора, которое я объяснил выше, является особенным для ->
и отличается от поведения любого другого бинарного оператора. Это не то, что вы можете вывести из общего правила перегрузки бинарных операторов.
Спасибо за ответ! Хотя я все еще немного в замешательстве. Почему компилятор переводит uniquePtr->member
в (uniquePtr.operator->())->member
? То есть из одного оператора ->
мы получаем два оператора ->
. Имеет ли это смысл?
Извините, но я не считаю, что мой вопрос является дубликатом, поскольку предоставленный «дубликат» вопрос НЕ отвечает на мой.
@user25687822 user25687822 Я не закрыл ваш вопрос. Это был другой пользователь. Однако этот связанный вопрос считается каноническим дубликатом для всех вопросов по объяснениям базовой перегрузки операторов, даже если объяснения ->
в ответах кажутся не очень хорошими.
@user25687822 user25687822 Компилятор переводит это таким образом, потому что такого поведения требует стандарт. uniquePtr->member
сначала переводится в (эквивалент) (uniquePtr.operator->())->member
. Если возвращаемый тип operator->()
является объектом, который также имеет operator->()
, выражение второй раз преобразуется в (uniquePtr.operator->().operator->())->member
. Этот перевод в вызовы operator->()
повторяется до тех пор, пока один из operator->()
не вернет необработанный указатель (типа SomeType *
). [что, по сути, и сказал пользователь 17732522].
Дубликат ДЕЙСТВИТЕЛЬНО отвечает на ваш вопрос. Если вы выполните поиск
operator->
на этой странице, вы найдете короткий абзац, объясняющий, как работает перегрузкаoperator->
. Он даже использует пример типа указателя, который говорит, что вы должны вернутьvalue_type*
, как и в вашем случае.