2016-08-10 11 views
23

Когда 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; 
} 
+5

По умолчанию это оператор «побитового сдвига влево», который работает на типах типа «int». Это встроенный объект. Если '<<' перегружен, то он может использоваться для других целей. – iammilind

+7

IMO это основной приоритет и перегрузка оператора, не стоит вопрос. 'когда оператор << ссылается на оператор ввода и когда он ссылается на побитовый сдвиг влево? (C++) 'Когда типы операндов, подчиненные приоритету/ассоциативности, четко указывают язык, который перегружен для использования. –

+0

Возможный дубликат [Перегрузка оператора] (http://stackoverflow.com/questions/4421706/operator-overloading) –

ответ

14

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

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

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

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

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

4

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

9

Этот вызов:

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

сначала рассмотрим:

cout << a.b() 

, которые соответствуют оператору вставки и возвращает refence к COUT. Таким образом, команда будет:

(returned reference to cout) << a.a.b 

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

Если инструкция была:

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

часть в скобках будет считаться первых:

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

на этот раз у вас есть оператор между 2 int: компилятор может разрешить его только как вызов побитового оператора.

9

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

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

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

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

40

На этом выходе 10, а оператор < < ссылается на сдвиг влево.

< < соиЬ a.b() < < < < a.a.b епсИ;

Это связано с тем, что порядок оценки операндов не указан. С лязгом она выводит 11, но с GCC выводит 10.

Ваш код:

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

можно заменить:

std::cout.operator<<(a.b()).operator<<(a.a.b); 

лязг сначала вычисляет a.b() затем a.a.b, г ++ это делает другой наоборот. Так как ваш a.b() изменяет переменные, вы получаете разные результаты.

Когда вы переписать код, как:

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

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

+3

Concur. Выбор OP, использующий строго «1» и «0», является неудачным, поскольку он облако теряет базовый уровень eval-order. – WhozCraig

16

В вашем случае все 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 in C++ 17 11 будет единственным допустимым выходом для обоих случаев, поскольку он обеспечивает оценку параметров функции слева направо.

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

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

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

Действительно, порядок оценки будет хорошо определен в C++ 17.Это недоразумение, по-видимому были предсказаны авторами p0145:

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

+0

Будет ли C++ 17 указывать порядок оценки параметров функции? – NathanOliver

+3

@NathanOliver Нет, Антон в этом не прав. Изменение заключается в том, что 'a.a.b' не будет _interleaved_ с' operator << (cout, a.b()) '. Поэтому либо 'operator << (cout, a.b())' будет полностью оценен до 'a.a.b', либо наоборот. Так, например, порядок оценки 'cout', а затем' a.a.b', а затем 'a.b()' больше не будет соответствовать. – Oktalist

+1

@Oktalist Спасибо. Я думаю, что нашел [документ] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r2.pdf) за это – NathanOliver