Подтвердить что ты не робот

С++, почему оператор присваивания должен возвращать константу ref, чтобы избежать (a = b) = c

Я читаю книгу о С++ и точнее о перегрузке оператора.

Пример следующий:

const Array &Array::operator=(const Array &right)
{
// check self-assignment
// if not self- assignment do the copying
return *this; //enables x=y=z
}

Объяснение, представленное в книге о возврате const ref вместо ref, заключается в том, чтобы избежать присвоений, таких как (x = y) = z. Я не понимаю, почему мы должны избегать этого. Я понимаю, что в этом примере сначала оценивается x = y, и поскольку он возвращает ссылку на const, то команда = z не может быть выполнена. Но почему?

4b9b3361

Ответ 1

(x=y) означает x.operator=(y), который возвращает объект x. Следовательно, (x=y)=z означает (x.operator=(y)).operator=(z). Выражение в parens устанавливает x в y и возвращает x, а затем внешний бит устанавливает x в z. Он не устанавливает y в z, как вы могли ожидать, и как это делает выражение x = y = z.

Это поведение противоречит интуиции (они должны быть равны после присваивания, правильно?); возвращение ссылки const делает невозможным и позволяет избежать проблемы.

Ответ 2

Нет необходимости избегать этого, если книга не предназначена для программистов, которые обычно пишут (x=y)=z, когда они означают x=y=z. На практике никто в здравом уме не пишет об этом, поэтому предосторожность совершенно не нужна. Он также запрещает некоторые другие строгие конструкции, такие как (x=y).nonConstMember(), которые вряд ли кто-нибудь пишет, но которые могут быть полезны в некоторых контекстах (хотя они не должны быть чрезмерно использованы).

@ybungalobill прав, получите лучшую книгу.

Ответ 3

Насколько я знаю, операторы присваивания не возвращают ссылки const в идиоматическом С++. Стандартные типы также не возвращают ссылки на const.

std::string a, b, c;
(a = b).clear(); // no objection from compiler

Все мои пользовательские операторы присваивания возвратили неконстантную ссылку.

В случае сомнений проверьте стандартную библиотеку. Это не безупречно, но он определенно получает такие основные вещи, как это правильно.

Ответ 4

Я бы посмотрел на поведение встроенных типов.

При определении ваших собственных типов предпочтительно, чтобы операторы вели себя так же, как и встроенные типы. Это позволяет легко принимать ваши классы, не вникая в ваш код, чтобы понять, почему они ведут себя иначе, чем ожидалось.

Итак, если мы посмотрим на целые числа:

int main()
{
    int x = 5;
    int y = 6;
    int z = 7;

    (x = y) = z;
    std::cout << x << " " << y << " " << z << "\n";
}

Это работает с y без изменений и x назначается 7. В вашем коде я бы ожидал, что ваш оператор присваивания будет работать одинаково. Определение оператора стандартного назначения:

Array& Array::operator=(Array const& rhs)
{
    /* STUFF */
    return *this;
}

Делайте это просто отлично (предполагая/* STUFF */правильно).

Ответ 5

Единственная причина, по которой я вижу, это то, что эта книга была написана для объяснения программистов на С++ для C (или автора, понимание которого лучше, чем понимание С++). Поскольку для программиста C выражение (x = y) = z недопустимо для встроенных типов, и он, вероятно, попытается получить такое же поведение с его определяемыми пользователем типами.

Однако C и С++ - разные языки, а в С++ выражение (x = y) = z допустимо даже для встроенных типов. Поэтому, если вы хотите иметь такое же поведение для своих пользовательских типов, вы должны вернуть неконстантную ссылку в operator =.

Я бы посоветовал вам получить лучшую книгу, которая не создает путаницы между C и С++. Они не являются теми же языками, даже если они происходят из общей базы.