Путаница по поводу того, что результат перегруженного оператора присваивания является lvalue или нет

У меня есть некоторые сомнения, читая этот абзац из книги «Beginning Visual C++ 2013»:

Когда вы выражаете оставшуюся операцию присваивания как явный вызов перегруженной функции, это в конечном итоге становится:

(motto1.operator=(motto2)).operator=(motto3);

Теперь у вас есть ситуация, когда объект, возвращаемый функцией operator=(), используется для вызова функции operator=(). Если тип возвращаемого значения просто CMessage, это будет недопустимо, поскольку возвращается временная копия исходного объекта, которое будет значением r, а компилятор не разрешит вызов функции-члена с использованием Rvalue. Единственный способ гарантировать, что это скомпилируется и будет работать правильно, — вернуть ссылку, которая является lvalue.

Класс CMessage определяется следующим образом:

class CMessage
{
private:
    char* pmessage;

public:
    void ShowIt() const
    {
        cout << endl << pmessage;
    }

    CMessage(const char* text = "Default message")
    {
        cout << endl << "Constructor called.";
        pmessage = new char[strlen(text) + 1];
        strcpy(pmessage, text);
    }

    CMessage(const CMessage& initM)
    {
        cout << "Copy constructor called" << endl;
        pmessage = new char[strlen(initM.pmessage) + 1];
        strcpy(pmessage, initM.pmessage);
    }

    ~CMessage()
    {
        cout << "Destructor called." << endl;
        delete[] pmessage;
    }

    CMessage operator=(const CMessage& aMess)
    {
        delete[] pmessage;
        pmessage = new char[strlen(aMess.pmessage) + 1];
        strcpy(this->pmessage, aMess.pmessage);
        return *this;
    }
};

вместе с функцией main, определенной как:

int main()
{
    CMessage motto1, motto2;
    CMessage motto3("A miss is as good as a mile.");

    (motto1 = motto2) = motto3;

    motto1.ShowIt();
    motto2.ShowIt();
    motto3.ShowIt();
}

Вопрос: действительно ли операторная функция возвращает rvalue или книга просто ошибается в этом утверждении?

Насколько я понимаю, код ошибочен, потому что в конечном результате motto1 не будет изменено, но в остальном это совершенно законно, потому что возвращаемая копия (включая конструктор копирования) является lvalue.


person Grzegorz Szpetkowski    schedule 10.05.2016    source источник
comment
Связанный вопрос: stackoverflow.com/questions/6111905 /c-is-return-value-al-value   -  person Grzegorz Szpetkowski    schedule 10.05.2016


Ответы (2)


CMessage operator=(const CMessage& aMess) возвращает по значению. Это означает, что возвращается временный объект, а категория значения (motto1 = motto2)prvalue. Обратите внимание, что здесь нет ничего особенного в operator=, это было бы то же самое для любой функции, возвращающей значение.

Насколько я понимаю, код ошибочен, потому что в конечном результате девиз1 не будет изменен, но в остальном это совершенно законно,

В ISO C++ это правда...

потому что возвращаемая копия (с участием конструктора копирования) является lvalue.

Возвращаемая копия является временным объектом. Слово "lvalue" представляет собой категорию значений выражений; это не прилагательное, которое относится к объектам. Категория значения выражения, состоящего из вызова функции, когда эта функция возвращает значение, — это prvalue, а не lvalue.

компилятор не допустит вызова функции-члена с использованием rvalue.

В ISO C++ нормально вызывать функцию-член для rvalue. Я не могу говорить с Visual C++ 2013.

person M.M    schedule 10.05.2016
comment
примечание: страница MSDN lvalues ​​и rvalues содержит много основных ошибок в описательный текст (хотя примеры кода в порядке), я бы рекомендовал игнорировать его. страница stackoverflow лучше - person M.M; 10.05.2016
comment
Я подтверждаю, что код отлично компилируется в VS 2013/2015. Спасибо за совет про MSDN и ссылку. - person Grzegorz Szpetkowski; 10.05.2016

Действительно ли операторная функция возвращает значение r, или книга просто ошибается в этом утверждении?

Да, этот конкретный перегруженный оператор возвращает значение, поэтому категория значения выражения вызова — это (p)rvalue. Книга права в этом.

Насколько я понимаю, код ошибочен, потому что в конечном результате девиз1 не будет изменен.

Если намерение состоит в том, чтобы присвоить motto3 motto1, тогда да, код действительно ошибочен. Однако причина, которую приводит книга:

компилятор не разрешит вызов функции-члена с использованием rvalue

Это не правда. Это верно для C и, возможно, для достандартных версий C++, но не для стандартного C++.

person eerorika    schedule 10.05.2016