Когда делает operator <<
обратитесь к оператору вставки, и когда это относится к сдвигу влево?
Это будет выводить 10
, а также operator <<
относится к левому сдвигу.
cout << a.b() << a.a.b << endl;
И это будет выводить 11
, operator <<
относится к оператору вставки.
cout << a.b();
cout << a.a.b ;
Я запуталась, когда будет operator <<
(при использовании с cout
) обратитесь к левому оператору сдвига?
#include <iostream>
using namespace std;
class A {
public:
A() { a.a = a.b = 1; }
struct { int a, b; } a;
int b();
};
int A::b(){
int x=a.a;
a.a=a.b;
a.b=x;
return x;
};
int main(){
A a;
a.a.a = 0;
a.b();
cout << a.b() << a.a.b << endl; // ?????
return 0;
}
Проблема, с которой вы столкнулись, не связана с << оператор. В каждом случае оператор вставки вызывается.
Однако вы столкнулись с проблемой, касающейся порядок оценки в командной строке
cout << a.b() << a.a.b << endl;
Функция a.b()
имеет побочный эффект. Он меняет значения a.a.a и a.a.b. Таким образом, очевидно, что a.b () вызывается до или после оценки значения ov a.a.b
,
В C ++ порядок оценки не указан, см. cppreference.com для более подробного обсуждения.
Это выведет 10, и оператор<< обратитесь к левому сдвигу.
соиЬ << a.b () << a.a.b << епсИ;
Это связано с тем, что порядок вычисления операндов не определен. С clang выводит 11, а с gcc — 10.
Ваш код:
cout << a.b() << a.a.b << endl;
можно заменить на:
std::cout.operator<<(a.b()).operator<<(a.a.b);
Clang сначала оценивает a.b()
затем a.a.b
G ++ делает это наоборот. Так как ваш a.b()
изменяя переменные, вы получаете разные результаты.
Когда вы переписываете свой код как:
cout << a.b();
cout << a.a.b ;
тогда у вас есть два оператора полного выражения, здесь нет неопределенного поведения, связанного с вычислением операнда. Таким образом, вы всегда получаете один и тот же результат.
В твоем случае все operator <<
s являются операторами вставки выходного потока, потому что их левый аргумент имеет тип ostream&
и они группируются слева направо.
Разница в выводе обусловлена порядком вычисления аргументов функции:
cout << a.b() << a.a.b
является
operator<<(operator<<(cout, a.b()), a.a.b)
поэтому выход зависит от того, какой из a.a.b
или же a.b()
оценивается первым. Это на самом деле не определено текущим стандартом (C ++ 14), чтобы вы могли получить 11
также.
AFAIK в C ++ 17 11
будет единственным допустимым выходным сигналом для обоих случаев, потому что он обеспечивает оценку параметров функции слева направо.
Обновление: это, кажется, не соответствует действительности, как решил комитет (по состоянию на N4606) идти с неопределенно последовательной оценкой параметров, упомянутой внизу P0145R2. См. [Expr.call] / 5.
Обновление 2: поскольку здесь мы говорим о перегруженных операторах, применяется [over.match.oper] / 2 в N4606, в котором говорится
Однако операнды упорядочены в порядке, установленном для встроенного оператора.
Таким образом, порядок оценки будет хорошо определен в C ++ 17. Это недоразумение, по-видимому, было предсказано авторами P0145:
Мы не считаем, что такой недетерминизм приносит какую-либо существенную дополнительную выгоду оптимизации, но он увековечивает путаницу и риски, связанные с порядком оценок в вызовах функций
Этот звонок:
cout << a.b() << a.a.b << endl;
сначала рассмотрим:
cout << a.b()
которые соответствуют оператору вставки и возвращают ссылку на cout. Таким образом, инструкция станет:
(returned reference to cout) << a.a.b
который снова вызовет оператор вставки и так далее …
Если ваша инструкция была:
cout << (a.b() << a.a.b) << endl;
часть между скобками будет считаться первой:
a.b() << a.a.b
на этот раз у вас есть оператор между 2 int
Компилятор может разрешить его только как вызов побитового оператора.
Бинарные операторы, такие как <<
, имеют два свойства, которые определяют их использование: (оператор) приоритет и (слева или справа) ассоциативность. В этом случае ассоциативность является ключом, и, см., Например, http://en.cppreference.com/w/c/language/operator_precedence, <<
Оператор имеет ассоциативность слева направо, поэтому они упорядочены (как в скобках) слева направо:
((cout << a.b()) << a.a.b) << endl;
или в словах, упорядоченных как cout << a.b()
затем << a.a.b
а потом << endl
,
После этой последовательности перегрузка оператора вступает в силу при каждом вызове <<
с заданными типами, который затем определяет, какая перегрузка вызывается и, таким образом, если это cout
-операция или смена.
Без скобок операнды по обе стороны от <<
определить значение: int << int == shift
, stream << any == insertion
,
Это «повторное использование» оператора может быть запутанным, нежить. Но вы можете решить неясности, используя скобки: stream << (int << int) == "int"