Аргумент назначения перемещения выдает ошибку при разыменовании, но работает, когда к члену обращаются напрямую

Почему в минимально необходимом фрагменте кода в задании перемещения закомментированная строка *arg = nullptr; незаконна и arg.p = nullptr; в порядке? Если я правильно понимаю, оба изменяют rvalue, но при компиляции *arg = nullptr; выдает ошибку:

левый операнд должен быть l-значением

Я ожидал, что *arg предоставит доступ к argp из-за перегрузки операторов.

#include <iostream>

using namespace std;

class Ptr {
public:
    Ptr(double arg) :p{ new double(arg) } {}
    // copy constructor
    Ptr(const Ptr& arg) {
        p = new double(*(*arg));
    }
    // move assignment
    Ptr& operator=(Ptr&& arg) {
        delete p;
        p = *arg;
        cout << "move type of *arg " << typeid(*arg).name() << endl;
        arg.p = nullptr;
        //*arg = nullptr; // illegal
        return *this;
    }
    double* operator*() {
        return p;
    }
    const double* operator*() const {
        return p;
    }
    ~Ptr() {
        delete p;
    }
private:
    double* p;
};

Ptr copy(Ptr a) { return a; }


int main() {
    Ptr pt{ 2 };
    Ptr pt2{ 3 };
    pt2 = copy(pt);
    return 0;
}

Попробовал обратиться за помощью к

Обновлено: Удален вводящий в заблуждение комментарий к operator*. Я хотел бы сохранить код как есть. Проблема частично связана с PPP3, глава 17, упражнением Бьярна Страуструпа. Меня интересует ПОЧЕМУ.

позволяет читать и писать *p Нет. Вы хотели double*& operator*().

3CxEZiVlQ 21.08.2024 04:16

«Попробовал помощь в ChatGpt...» ИИ не является хорошим/надежным источником проблем с кодированием, особенно для такого языка, как C++.

user12002570 21.08.2024 04:25

Возврат вашего перегруженного operator* по значению означает, что вызов этих функций является выражением rvalue.

user12002570 21.08.2024 04:27

«Оба изменяют rvalue». Нет, arg.p — это выражение lvalue, даже arg само по себе является выражением lvalue, хотя его объявленный тип — «ссылка на rvalue». Это заблуждение неоднократно рассматривалось в SO.

Weijun Zhou 21.08.2024 04:31

«Если я правильно понимаю, оба изменяют rvalue». Имя «rvalue» происходит от «правой стороны значения присвоения». Вы спрашиваете о левой части задания (которое привело к названию lvalue)...

JaMiT 21.08.2024 04:44

@JaMiT Я интерпретирую это как «оба изменяют rvalue, поэтому не может быть так, чтобы работал только один из них».

Weijun Zhou 21.08.2024 04:57
operator*() возвращает копию адреса памяти p, который представляет собой шестнадцатеричное целое число. Чтобы вернуть это по ссылке, верните double*&.
Chukwujiobi Canon 21.08.2024 05:14

@WeijunZhou «Я интерпретирую это как [...]» — вы можете интерпретировать это так, если хотите. Это не имеет никакого отношения к моей точке зрения о том, что «изменение значения r (путем присвоения)» должно немедленно вызвать подозрение, что заявленное понимание может быть неверным.

JaMiT 21.08.2024 08:20

Теперь я понимаю, что комментарий к operator* неверен, однако я все равно хотел бы оставить код как есть. Проблема частично связана с главой 17 PPP3, упражнение Бьярна Страуструпа.

Bruno Mraz 21.08.2024 10:49

@BrunoMraz «Меня интересует ПОЧЕМУ...» Я объяснил почему в своем ответе ниже. В настоящее время *arg является выражением rvalue. Если вы измените тип возвращаемого значения на ссылку lvalue, он станет выражением lvalue.

user12002570 21.08.2024 11:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я ожидаю, что *arg предоставит доступ к arg p из-за перегрузки операторов.

Проблема в том, что ваш перегруженный возврат operator* по значению, что означает вызов этих функций, как в *arg, является выражением rvalue.

Чтобы решить эту проблему, вы можете вернуть указатель по ссылке lvalue, как показано ниже.

//---------v------------------>return by reference
    double*& operator*() {
        return p;
    }

Рабочая демо

Почему отрицательные голоса? В ответе нет ничего плохого. Кнопка «против» предназначена для бесполезного ответа, хотя этот ответ очень полезен.

user12002570 21.08.2024 09:55

Я хотел бы уточнить, правильно ли я понимаю, что я могу интерпретировать operator* как вызов функции, подобный этому Ptr::operator*(pt), и поскольку pt извлекло свое rvalue при назначении перемещения из-за Ptr&& arg, operator* продолжает возвращать rvalue, которое является данными, а не переменной. С другой стороны, arg.p осуществляет доступ к члену, где arg — это rvalue, а все, что следует за dot, — это lvalue, потому что именно так ведет себя C++ как язык. Я на правильном пути с этим объяснением? Если да, то я мог бы сказать, что в данном случае это просто вопрос синтаксиса C++.

Bruno Mraz 21.08.2024 11:06

@BrunoMraz Это намного проще. Например, всякий раз, когда у нас есть функция, которая возвращает значение, например, int func();, тогда вызов этой функции, например func(), является rvalue. Теперь скажем, что если у нас есть другая функция, такая как int& bar();, то вызов этой второй функции, такой как bar(), является выражением lvalue. Они взяты из определений lvalue, rvalue и т. д. Аналогично, для оператора точки . существуют разные правила для операндов и его результата. В вашем примере arg.p — это выражение lvalue.

user12002570 21.08.2024 11:13

Теперь я понял, спасибо за объяснение. Переход от double* к double*& в operator* теперь имеет для меня смысл.

Bruno Mraz 21.08.2024 11:24

@BrunoMraz Пожалуйста :)

user12002570 21.08.2024 11:38

Другие вопросы по теме