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

Когда оператор << ссылается на оператор ввода и когда на побитовый сдвиг влево?

Когда 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;
}
4b9b3361

Ответ 1

Проблема, с которой вы сталкиваетесь, не связана с < оператор. В каждом случае вызывается оператор вставки.

Однако вы столкнулись с проблемой, связанной с порядком оценки в командной строке

cout << a.b() << a.a.b << endl;

Функция a.b() имеет побочный эффект. Он меняет значения a.a.a и a.a.b. Таким образом, очевидно, что a.b() вызывается до или после оценки значения ov a.a.b.

В С++ порядок оценки не указан, см. cppreference.com для более подробного обсуждения.

Ответ 2

Это будет выводить 10, а оператор < см. сдвиг влево.

cout < 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 ;

тогда у вас есть два оператора выражения полного выражения, здесь нет неопределенного поведения, связанного с оценкой операнда. Таким образом, вы получаете тот же результат.

Ответ 3

В вашем случае все operator << являются операторами ввода потока вывода, поскольку их левый аргумент имеет тип ostream&, и они группируются слева направо.

Разница в выходе вызвана порядком оценки аргументов функции:

cout << a.b() << a.a.b

является

operator<<(operator<<(cout, a.b()), a.a.b)

поэтому выход зависит от того, какой из параметров a.a.b или a.b() оценивается первым. Это фактически не определено текущим стандартом (С++ 14), чтобы вы могли получить 11.

AFAIK в С++ 17 11 будет единственным допустимым выходом для обоих случаев, поскольку он обеспечивает оценку параметров функции слева направо.

Обновление: это, похоже, неверно, поскольку комитет решил (с N4606) перейти с неопределенным секвенированием оценка параметров, упомянутая внизу P0145R2. См. [Expr.call]/5.

Update2: Поскольку мы говорим о перегруженных операторах здесь, применяется [over.match.oper]/2 в N4606, в котором говорится

Однако операнды упорядочены в порядке, предписанном для встроенного оператора.

Таким образом, порядок оценки будет хорошо определен в С++ 17. Это недоразумение, по-видимому, было предсказано авторами P0145:

Мы не считаем, что такой недетерминированность приносит существенную добавленную выгоду оптимизации, но она увековечивает путаницу и опасности вокруг порядка оценок в вызовах функций

Ответ 4

Этот вызов:

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: компилятор может разрешить его только как вызов побитового оператора.

Ответ 5

Двоичные операторы, такие как <<, имеют два свойства, которые определяют их использование: (оператор) приоритет и (левая или правая) ассоциативность. В этом случае ассоциативность является ключом, и, см., Например, http://en.cppreference.com/w/c/language/operator_precedence, оператор << имеет ассоциацию слева направо, поэтому они упорядочены (как бы скобки) слева направо:

((cout << a.b()) << a.a.b) << endl;

или в словах, обозначенных как cout << a.b(), затем << a.a.b, а затем << endl.

После этого секвенирования перегрузка оператора вступает в силу при каждом вызове << с заданными типами, который затем определяет, какая перегрузка вызывается, и, следовательно, если это a cout -операция или сдвиг.

Ответ 6

Без скобок операнды с обеих сторон << определяют значение: int << int == shift, stream << any == insertion. Это "повторное использование" оператора может быть запутанным, indead. Но вы можете решить двусмысленности с помощью круглых скобок: stream << (int << int) == "int"