2010-04-21 10 views
1

Я использую оператор потока < < и оператор смещения битов < < в одной строке. Я немного смущен, почему код А) не производит тот же вывод, что и код В)?оператор <<: std :: cout << i << (i << 1);

)

int i = 4; 
std::cout << i << " " << (i << 1) << std::endl; //4 8 

B)

myint m = 4; 
std::cout << m << " " << (m << 1) << std::endl; //8 8 

класс Минт:

class myint { 
    int i; 
public: 
    myint(int ii) { 
     i = ii; 
    } 
    inline myint operator <<(int n){ 
     i = i << n; 
     return *this; 
    } 
    inline operator int(){ 
     return i; 
    } 
}; 

заранее спасибо
Нам

+0

Почти дубликат: http://stackoverflow.com/questions/2603312/the-result-of-int-c0- coutcc /. Для большинства практических целей они одинаковы, хотя в качестве оператора, который сделал модификацию, использовали «++» вместо «<<». –

+2

@ Jerry Coffin: они очень похожи, но есть целые << vs < <путаница и тот факт, что << не меняет условные аргументы обычным образом. –

ответ

8

Ваш второй пример - неопределенное поведение.

Вы назначили оператора << на свой myint класс, как если бы это было на самом деле <<=. Когда вы выполняете i << 1, значение в i не изменяется, но когда вы выполняете m << 1, значение в mравно изменено.

В C++ это неопределенное поведение как для чтения, так и для записи (или записи более одного раза) в переменную без промежуточной точки последовательности, которые вызовы функций и операторы не относятся к их аргументам. Это недетерминировано ли код

std::cout << m << " " << (m << 1) << std::endl; 

будет выводить первые m до или после m обновляются m << 1. Фактически, ваш код может делать что-то совершенно странное, или сбой. Неопределенное поведение может привести к буквально всему, поэтому избегайте этого.

Одним из подходящих способов определения оператора << для myint является:

myint operator<< (int n) const 
{ 
    return myint(this->i << n); 
} 

(this-> не является строго необходимым, только мой стиль, когда я перегружать операторы)

+1

Но перегруженные операторы - это вызовы функций. Есть * lots * точек последовательности. –

+2

'std :: cout << m <<" "<< (m << 1)' эквивалентно 'std :: cout.operator << (m) .оператор << (" ") .оператор << (m.operator << (1)) '. Существует точка последовательности между тем, когда вызывается каждый из внешних операторов, но не существует точки последовательности между оценками аргументов, которые передаются операторам, включая внутренний вызов 'operator <<'. Другими словами, в 'f (a()). g (b())' существует точка последовательности между вызовом 'f' и вызовом' g' , но порядок вызовов 'a' и' b' не определен. –

+0

Возможно, вам следует обновить свой ответ, потому что без объяснения в вашем комментарии, далеко не очевидно, что ни одна из многих точек последовательности не гарантирует, что между ключевыми выражениями не существует. –

2

Ваш < < Оператор фактически < < = оператор. Если вы замените строку с

std::cout << i << " " << (i <<= 1) << std::endl; //8 8 

вы должны получить 8 8.

+6

Вы выделили проблему, но ваш «ответ» имеет неопределенное поведение. –

1

Хорошо (м < < 1) вычисляется до м и, следовательно, м имеет 8 уже, как у оператора < < вы перезаписать собственного значения.

Это неправильное поведение на вашей стороне, оператор < < должен быть const и не изменять ваш объект.

5

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

Причина, по которой вы получаете 8 для первого, состоит в том, что, по-видимому, m < < 1 называется первым в вашей реализации. Реализация может выполнять их в любом порядке.

0

Поскольку << оператор myint изменяет его lhs. Поэтому после оценки m << 1 m фактически будет иметь значение 8 (тогда как i << 1 возвращает 8, но не делает i равным 8). Поскольку не указано, выполняется ли m<<1 до cout << m (поскольку он не указан в каком порядке оцениваются аргументы функции или оператора), не указано, будет ли выходной сигнал 8 8 или 4 8.

2

поскольку m является myInt вашего второго примера можно переписать в виде:

std::cout << m << " " << (m.operator<<(1)) << std::endl; 

Порядок оценки для подвыражения m и (m.operator<<(1)) не определены, поэтому нет изречения, которое «m» вы получите за 1-е выражение, которое используется m (что является простым выражением m). Таким образом, вы можете получить результат «4 8», или вы можете получить «8 8».

Обратите внимание, что оператор не приводит к непредсказуемому поведению, потому что есть последовательность точек (по крайней мере, одна функция вызова) между когда m изменяется, и когда это «читать». Но порядок оценки подвыражений неуточнен, поэтому, пока компилятор должен произвести результат (он не может сбой - по крайней мере, не законно), нет никакого упоминания, какой из двух возможных результатов он должен произвести.

Таким образом, утверждение примерно так же полезно, как и одно неопределенное поведение, то есть это не очень полезно.

0

Язык C++ не определяет порядок оценки операторов. Он определяет только их ассоциативность.

Поскольку ваши результаты зависят от того, когда функция operator<< оценивается внутри выражения, результаты не определены.

Алгебраические operator $ функции всегда должны быть const и возвращает новый объект:

inline myint operator <<(int n) const { // ensure that "this" doesn't change 
    return i << n; // implicit conversion: call myint::myint(int) 
} 
Смежные вопросы