Форум программистов, компьютерный форум CyberForum.ru

Странное рекурсивное поведение объекта std::cout - C++

Восстановить пароль Регистрация
 
 
xtorne21st
интересующийся
300 / 271 / 19
Регистрация: 25.09.2010
Сообщений: 1,056
09.02.2013, 17:34     Странное рекурсивное поведение объекта std::cout #1
Пытался организовать очередь при помощи шаблона и наткнулся на "странное" поведение:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Организация очереди при помощи шаблона
 
#include <iostream>
#include <cstdlib>
 
// По умолчанию тип int, размер 100
template <typename QType = int, int size = 100>
class Queue {
    QType a[size];
    int left, right;
 
    public:
    Queue();
    void qput(QType val);
    QType qget();
};
 
template <typename QType, int size>
Queue<QType, size>::Queue()
{
    left = right = 0;
}
 
template <typename QType, int size>
void Queue<QType, size>::qput(QType val)
{
    if (right == size) {
        std::cout << "Queue is full.\n";
        return; 
    }
    a[++right] = val;
}
 
template <typename QType, int size>
QType Queue<QType, size>::qget()
{
    if (left == right) {
        std::cout << "Queue is empty: ";
        return 0;
    }
    return a[++left];
}
 
int main()
{
    Queue<char> a;
 
    a.qput('e');
    a.qput('n');
    a.qput('g');
    a.qput('l');
    a.qput('i');
    a.qput('s');
    a.qput('h');
 
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget() << '\n';
 
    // Now queue is empty
    // another one
    a.qput('e');
    a.qput('n');
    a.qput('g');
    a.qput('l');
    a.qput('i');
    a.qput('s');
    a.qput('h');
 
    std::cout << a.qget() << a.qget() << a.qget() << a.qget()
        << a.qget() << a.qget() << a.qget() << '\n';
 
    return 0;
}
Bash
1
2
3
4
ilyuha21st@coldshoot:~/projects$ ./prog
english
hsilgne
ilyuha21st@coldshoot:~/projects$
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
09.02.2013, 17:34     Странное рекурсивное поведение объекта std::cout
Посмотрите здесь:

cout vs. std::cout C++
C++ std::endl; и std::cout"/n"; - одно и то же?
C++ Не работает std::cout || std::cin
Странное поведение cout после объявления объекта класса C++
C++ Стандартный поток и STL (std::copy to std::cout)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Kaimi
 Аватар для Kaimi
36 / 31 / 3
Регистрация: 17.09.2012
Сообщений: 66
09.02.2013, 18:30     Странное рекурсивное поведение объекта std::cout #2
C++
1
2
3
4
5
6
7
8
#include <iostream>
 
int main()
{
    int i = 0;
    std::cout << i * 2 << ++i;
    return 0;
}
И на экране видим 21.

Идея понятна?
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
09.02.2013, 18:40     Странное рекурсивное поведение объекта std::cout #3
Цитата Сообщение от Kaimi Посмотреть сообщение
И на экране видим 21.
Идея понятна?
Тут дело в другом, действительно интересное поведение. Казалось бы, это тоже самое, что
C++
1
std::cout << 'e' <<  'n' ; // etc
но нет, компилятор генерит разный код.
Дизассемблировал в студии и в gcc, проблема не исчезла и суть получившегося кода одинакова (значит возможно это даже описано в стандарте, но лень туда лезть). Короче дело в следующем - в моем примере выше в стек кладется сначала 'n' потом 'e', т.е. внутри оператора << параметры будут извлекаться правильно, т.е. сначала 'e', потом 'n'. А вот этот код
C++
1
2
std::cout << a.qget() << a.qget() << a.qget() << a.qget()
        << a.qget() << a.qget() << a.qget() << '\n';
в ассемблере выглядет по-другому - аргументы кладутся в стек в порядке записи (т.е. 'e', 'n', 'g' и т.д.), значит внутри оператора << они будут извлечены в обратном порядке, что мы и видим на выходе.

На самом деле очень интересно, может кто-нибудь стандарт пошелестит?
Schizorb
 Аватар для Schizorb
508 / 460 / 16
Регистрация: 07.04.2012
Сообщений: 865
Записей в блоге: 1
Завершенные тесты: 1
09.02.2013, 19:09     Странное рекурсивное поведение объекта std::cout #4
Если не ошибаюсь, порядок вычисления операндов неопределен, кроме операций &&, || и ?:. То есть какой a.qget() будет выполнен первым, а какой последним - на усмотрение компилятора.
Jupiter
09.02.2013, 19:11
  #5

Не по теме:

Цитата Сообщение от Kastaneda Посмотреть сообщение
На самом деле очень интересно, может кто-нибудь стандарт пошелестит?
баян, можно и нагуглить, даже на этом форуме уже 100500 раз было

Croessmah
Модератор
Эксперт С++
 Аватар для Croessmah
11845 / 6824 / 771
Регистрация: 27.09.2012
Сообщений: 16,919
Записей в блоге: 2
Завершенные тесты: 1
09.02.2013, 19:12     Странное рекурсивное поведение объекта std::cout #6
Цитата Сообщение от Schizorb Посмотреть сообщение
Если не ошибаюсь, порядок вычисления операндов неопределен, кроме операций &&, || и ?:.
Точки следования
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
09.02.2013, 19:18     Странное рекурсивное поведение объекта std::cout #7
Цитата Сообщение от Jupiter Посмотреть сообщение
баян, можно и нагуглить, даже на этом форуме уже 100500 раз было
Так это UB или нет?

Не по теме:

Если честно, некогда гуглить, меня тут с компа гонят, сопротивлятся бесполезно



Тут дело даже не в порядке вычисления аргументов, т.к.
C++
1
2
std::cout << a.qget() << a.qget() << a.qget() << a.qget()
        << a.qget() << a.qget() << a.qget() << '\n';
тут же каждый << это отдельный вызов оператора, т.е. теоретически это эквивалентно
C++
1
2
3
4
5
6
7
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget();
    std::cout << a.qget() << '\n';
но практически мы видим обратное - сначала вызываются все a.qget(), а потом вызываются все operator<<().
Croessmah
Модератор
Эксперт С++
 Аватар для Croessmah
11845 / 6824 / 771
Регистрация: 27.09.2012
Сообщений: 16,919
Записей в блоге: 2
Завершенные тесты: 1
09.02.2013, 19:21     Странное рекурсивное поведение объекта std::cout #8
Цитата Сообщение от Kastaneda Посмотреть сообщение
тут же каждый << это отдельный вызов оператора, т.е. теоретически это эквивалентно
Нет. В первом случае порядок вызова функций не определен и зависит от компилятора.
Во-втором, же у нас стоит ; которая ставит точку следования
OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
09.02.2013, 19:22     Странное рекурсивное поведение объекта std::cout #9
Цитата Сообщение от Kastaneda Посмотреть сообщение
На самом деле очень интересно, может кто-нибудь стандарт пошелестит?
Enjoy your C++, что ещё сказать. Перегруженные операторы сохраняют приоритеты и ассоциативность соответствующих операторов, но при этом являются функциями со всеми вытекающими. Включая sequence point перед и после тела, но не между вычислениями операндов. Смотрите
C++
1
2
std::cout << 'e' <<  'n';
operator<<(operator<<(std::cout, 'e'), 'n');
и точно так же
C++
1
2
std::cout << a.qget() << a.qget() << a.qget();
operator<<(operator<<(operator<<(std::cout, a.qget()), a.qget()), a.qget());
Так вот, стандарт не обязывает вычислять аргументы в каком-то определённом порядке. Только чтоб они были вычислены до того, как пойдёт работа тела функции. Поэтому компилятор может как сначала вычислить внутренний operator<<(), а только потом вытянуть из очереди следующий элемент, так и наоборот — сначала вытянуть, запомнить его, и только потом приняться за operator<<(). Итого побочные эффекты типа "вывод в поток" оказываются упорядоченными правильно, а побочные эффекты "изменение состояния очереди" — в противоположном порядке. Так как вычисление значений обычных символов не имеет побочных эффектов, то с ними всё хорошо.

Справедливости ради, если б они оставались не функциями, а какими-то магическими "описателями операторов", то всё равно среди операторов sequence point есть только у запятой и тернарного.
Croessmah
09.02.2013, 19:40
  #10

Не по теме:

Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
sequence point есть только у запятой и тернарного.
у ленивых логических тоже есть

go
Эксперт C++
3582 / 1362 / 128
Регистрация: 16.04.2009
Сообщений: 4,528
09.02.2013, 19:56     Странное рекурсивное поведение объекта std::cout #11
Цитата Сообщение от Kastaneda Посмотреть сообщение
Так это UB или нет?
Анспецифик http://liveworkspace.org/code/ByMFa$0

Добавлено через 17 секунд
Цитата Сообщение от xtorne21st Посмотреть сообщение
на "странное" поведение:
Ничего странного здесь нет.
abit
 Аватар для abit
260 / 259 / 33
Регистрация: 03.02.2013
Сообщений: 709
09.02.2013, 20:09     Странное рекурсивное поведение объекта std::cout #12
сдаётся мне из-за таких побочных эффектов в STL-ных stack и queue функции вытаскивания элементов разделены на две - одна вытаскивает, вторая уничтожает его в структуре

C++
1
2
3
4
5
6
7
8
9
#include <queue>
....
 
std::queue<char> a;
 
a.push('e'); - добавляет в очередь
 
std::cout << a.front(); - возвращает элемент, но не уничтожает его в очереди
a.pop(); - ничего не возвращает, но уничтожает элемент в очереди
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
09.02.2013, 20:31     Странное рекурсивное поведение объекта std::cout #13
Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
Так вот, стандарт не обязывает вычислять аргументы в каком-то определённом порядке. Только чтоб они были вычислены до того, как пойдёт работа тела функции.
Да, все дело в этом. Я ступил.

Не по теме:

Цитата Сообщение от ~OhMyGodSoLong~ Посмотреть сообщение
Enjoy your C++, что ещё сказать.
Ага, последнее время 50% рабочего времени пишу на assemler и 50% на С++ и то там С++ почти С. Короче забывать стал, до устройства на работу (1.5 г. назад) я С++ знал куда лучше, чем сейчас


go, не понял, зачем ссылка на LWS? Там тоже самое.

Добавлено через 4 минуты
Цитата Сообщение от abit Посмотреть сообщение
сдаётся мне из-за таких побочных эффектов в STL-ных stack и queue функции вытаскивания элементов разделены на две - одна вытаскивает, вторая уничтожает его в структуре
нет
xtorne21st
интересующийся
300 / 271 / 19
Регистрация: 25.09.2010
Сообщений: 1,056
09.02.2013, 20:39  [ТС]     Странное рекурсивное поведение объекта std::cout #14
Цитата Сообщение от go Посмотреть сообщение
Ничего странного здесь нет.
ну для кого как). если не знатешь в чём дело (а я не знал вём дело), мне этот момент показался странным.
go
Эксперт C++
3582 / 1362 / 128
Регистрация: 16.04.2009
Сообщений: 4,528
09.02.2013, 21:01     Странное рекурсивное поведение объекта std::cout #15
Цитата Сообщение от Kastaneda Посмотреть сообщение
go, не понял, зачем ссылка на LWS? Там тоже самое.
http://liveworkspace.org/code/2jD21P$0

Цитата Сообщение от xtorne21st Посмотреть сообщение
мне этот момент показался странным.
Так с стандарте все есть
xtorne21st
интересующийся
300 / 271 / 19
Регистрация: 25.09.2010
Сообщений: 1,056
09.02.2013, 21:30  [ТС]     Странное рекурсивное поведение объекта std::cout #16
Цитата Сообщение от go Посмотреть сообщение
Так с стандарте все есть
Я бы с радостью начал изучать стандарт, но на данном этапе нахожу более полезным почитать какого-нибудь автора. А уж потом, когда большинство аспектов и приёмов дойдут до автоматизма, и обычное чтении книг уже не будет вызывать кокой-либо интерес, тогда уже можно будет начать изучать стандарт. Имхо.
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4237 / 2770 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
09.02.2013, 21:52     Странное рекурсивное поведение объекта std::cout #17
Цитата Сообщение от go Посмотреть сообщение
http://liveworkspace.org/code/2jD21P$0
go, а теперь для тех, кто сегодня тупит - в чем разница? Всмысле я вижу, что вывод отличается от предыдущего, но разницы в коде беглым взглядом не вижу.

Добавлено через 6 минут
Аааа, вижу снимаю вопрос.
xtorne21st
интересующийся
300 / 271 / 19
Регистрация: 25.09.2010
Сообщений: 1,056
09.02.2013, 22:31  [ТС]     Странное рекурсивное поведение объекта std::cout #18
Цитата Сообщение от Kastaneda Посмотреть сообщение
Аааа, вижу снимаю вопрос.
Так поделитесь пожалуйста с другими что вы там заметили, я лично скомпилировал этот код у себя на машине и результат как и прежде.
go
Эксперт C++
3582 / 1362 / 128
Регистрация: 16.04.2009
Сообщений: 4,528
09.02.2013, 22:32     Странное рекурсивное поведение объекта std::cout #19
Цитата Сообщение от Kastaneda Посмотреть сообщение
но разницы в коде беглым взглядом не вижу.
xtorne21st, А ее там нет. Код я скопировал полностью, только компилятор clang поставил.


xtorne21st, из стандарта С++
Цитата Сообщение от C++
The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.
Иными словами все зависит от компилятора.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.02.2013, 22:36     Странное рекурсивное поведение объекта std::cout
Еще ссылки по теме:

В чем разница std::cout и просто cout? C++
Std::cout<<(new class)->something(); C++
Странное поведение операции XOR и std::cin C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
xtorne21st
интересующийся
300 / 271 / 19
Регистрация: 25.09.2010
Сообщений: 1,056
09.02.2013, 22:36  [ТС]     Странное рекурсивное поведение объекта std::cout #20
единственное в чём разница, так это в компиляторе, clang и gcc с одними и теме же ключами генерирует разный код...

Добавлено через 2 минуты
Цитата Сообщение от go Посмотреть сообщение
The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.
Это вы с N3242 взяли? Можете указать страничку, пожалуйста

Добавлено через 37 секунд
Цитата Сообщение от go Посмотреть сообщение
Иными словами все зависит от компилятора.
Немножко меня опередили)
Yandex
Объявления
09.02.2013, 22:36     Странное рекурсивное поведение объекта std::cout
Ответ Создать тему
Опции темы

Текущее время: 21:19. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru