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

Почему (a = b) = c юридический синтаксис в С++?

Как работает оператор типа (a = b) = c; в С++, если a, b и c являются int или любым другим примитивным типом?

4b9b3361

Ответ 1

Неформально, в С++ для встроенных типов результат a = b является ссылкой на a; вы можете присвоить значение этой ссылке, как и с любой другой ссылкой. Поэтому (a = b) = c присваивает значение b a, а затем присваивает значение c a.

Для пользовательских типов это может не применяться, хотя обычная идиома заключается в том, что оператор присваивания возвращает ссылку на левый аргумент, поэтому поведение пользовательских типов имитирует поведение встроенных типов:

struct S {
    S& operator=(const S& rhs) {
        return *this;
    }
};

Теперь S a, b, c; (a = b) = c; означает вызов a.operator=(b), который возвращает ссылку на a; затем вызовите S::operator= по этому результату и c, эффективно называя a.operator=(c).

Ответ 2

Выражение присваивания a = b не является значением l в C, но оно находится в С++:

  • C11, 6.5.14 (Операторы присваивания):

    Оператор присваивания сохраняет значение в объекте, обозначенном левым операндом. Выражение присваивания имеет значение левого операнда после назначения, , но не lvalue.

  • С++ 14, 5.18 [expr.ass] (Операторы присваивания и составного присваивания):

    Оператор присваивания (=) и составные операторы присваивания все группы справа налево. Все они требуют модифицируемого lvalue в качестве своего левого операнда и возвращают lvalue, ссылаясь на левый операнд.

В эволюции С++ из C несколько выражений были сделаны "lvalue-aware" как бы потому, что lvalues ​​гораздо важнее в С++, чем в C. В C все тривиально (тривиально копируемо и тривиально разрушаемо, все в словах С++), поэтому преобразование lvalue-to-rvalue (или "lvalue conversionions", поскольку C называет их), не являются болезненными. В С++ копирование и уничтожение являются нетривиальными понятиями, и, если выражения сохраняют lvalue-ness, можно избежать большого копирования и разрушения, что никогда не было необходимо для начала.

Другим примером является условное выражение (a ? b : c), которое не является значением l в C, но может быть lvalue в С++.

Еще один интересный артефакт этой эволюции языка состоит в том, что C имеет четыре четко определенные длительности хранения (автоматический, статический, поточно-локальный, динамический), но на С++ это становится более запутанным, поскольку временные объекты - нетривиальная концепция в С++, который почти требует своей собственной продолжительности хранения. (Например, Clang внутренне имеет пятое, "полное выражение" время хранения). Временные переменные, конечно, являются результатом преобразования lvalue-to-rvalue, поэтому, избегая преобразования, есть еще одна вещь, о которой нужно беспокоиться.

(Обратите внимание, что все это обсуждение относится только к соответствующим выражениям основного языка. В С++ также есть отдельная, не связанная с этим функция перегрузки оператора, которая выдает выражения вызова функций, которые имеют всю обычную семантику вызовов функций и не имеют ничего для операторов, за исключением синтаксиса. Например, вы можете определить перегруженный operator=, который возвращает значение prvalue или void, если вы этого пожелаете.)

Ответ 3

(a = b) = c - допустимый оператор в С++. Здесь '=' работает как оператор присваивания. Здесь значение b будет присвоено значению a, а c будет присвоено значение a для права налево..

Например:

int a = 5;
int b = 2;
int c = 7;
int answer = (a = b) = c;
cout << answer << endl;

Вывод:

7

Ответ 4

Ниже приведено небольшое предположение, поэтому, пожалуйста, исправьте меня, если я ошибаюсь.

Когда они изобрели перегрузку оператора, им пришлось придумать стандартную форму оператора присваивания для любого класса T. Например:

T& T::operator=(T);
T& T::operator=(const T&);

Здесь он возвращает ссылку на T вместо просто T, чтобы сделать назначение из трех частей, например x = (y = z) эффективным, не требуя копии.

Он может вернуть ссылку const на T, что сделало бы нежелательное назначение (a = b) = c ошибкой. Я предполагаю, что они не использовали это по двум причинам:

  • Более короткий код - не нужно писать все эти const все время (тонкие детали const -корректности в то время не были ясными)
  • Большая гибкость - позволяет использовать код типа (a = b).print(), где print - это метод не const (потому что программист ленился/не знал о const -корректности)

Семантика для примитивных типов (которые не являются class es) были экстраполированы, чтобы дать:

int& operator=(int&, int); // not real code; just a concept

"Тип возврата" не const int&, поэтому он соответствует шаблону с class es. Таким образом, если код buggy (a = b) = c действителен для пользовательских типов, он должен быть действительным также для встроенных типов, как требуется Принципы проектирования С++. И как только вы документируете этот материал, вы не можете его изменить из-за обратной совместимости.