std::cout и static variable - порядок вывода в консоль
От: Valen  
Дата: 05.05.11 07:59
Оценка: 1 (1) :)
Здравствуйте.
int f(int i)
{
    static int in = 0; // <--- (#1)
    in += i;
    return ++(in);
}

int main()
{
    ....
    cout<<f(1)<<f(2)<<f(3)<<endl; // <--- (#2)
    //cout<<f2(1); cout<<endl<<f2(2); cout<<endl<<f2(3); //<--- (#3)
    ....
}

Вывод "9 7 4", т.е. в обратном порядке. В дебагере видно что сначала вызывается f(3), потом f(2), а затем f(1) что и объясняет порядок вывод цифр.

Если в (#1) закомментировать static, то порядок вызова остается прежним (f(3), f(2), f(1)), но вывод в консоль идет в прямом порядке — 1 2 3.

Если (#2) заменить на (#3), то вывод со static осуществляется в прямом порядке.

1. Я правильно понял, что строка cout<<f(x1)<<f(x2)<<f(x3) есть UB, т.е. в общем случае не известно в каком именно порядке будет осуществлен вывод?
2. Почему выводы осуществляются в разных порядках? Стандарт что-то говорит про вывод переменных, которые является результатами работы функции со статическими переменными?

win7, vs2010 + gcc 4.4.1

Спасибо.
Re: std::cout и static variable - порядок вывода в консоль
От: Erop Россия  
Дата: 05.05.11 08:10
Оценка:
Здравствуйте, Valen, Вы писали:

V>1. Я правильно понял, что строка cout<<f(x1)<<f(x2)<<f(x3) есть UB, т.е. в общем случае не известно в каком именно порядке будет осуществлен вывод?


Не UB, но порядок вызова функций не определён.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: std::cout и static variable - порядок вывода в консоль
От: uzhas Ниоткуда  
Дата: 05.05.11 08:26
Оценка:
Здравствуйте, Valen, Вы писали:

V>1. Я правильно понял, что строка cout<<f(x1)<<f(x2)<<f(x3) есть UB, т.е. в общем случае не известно в каком именно порядке будет осуществлен вывод?

порядок вызова функций не определен

V>2. Почему выводы осуществляются в разных порядках? Стандарт что-то говорит про вывод переменных, которые является результатами работы функции со статическими переменными?

порядок вывода одинаковый : http://ideone.com/2fmj0
Re[2]: std::cout и static variable - порядок вывода в консол
От: Valen  
Дата: 05.05.11 10:17
Оценка:
Здравствуйте, uzhas, Вы писали:

U>порядок вывода одинаковый : http://ideone.com/2fmj0

Точно, вопрос можно закрыть. Спасибо.
Re[2]: std::cout и static variable - порядок вывода в консол
От: Панда Россия  
Дата: 05.05.11 10:56
Оценка:
V>>Стандарт что-то говорит про вывод переменных, которые является результатами работы функции со статическими переменными?

Очень на многие подобные вопросы можно ответить исходя из принципов сборки проектов в С++ и факта отсутствия у компилятора телепатических способностей.

Очевидно, что стандарт не может говорить ничего про особенности вызовов функций со статическими переменными, потому что компилятору неоткуда узнать, есть ли внутри функции статические переменные. Функция не обязана быть определена в том же файле, она может быть в другом файле или в библиотеке. Более того, ее вообще может не быть на данном компьютере. Я могу, например, скомпилировать, скопировать получившийся объектный файл на флешку и понести на другой компьютер, чтобы там уже слинковать с файлами, в которых содержится тело этой функции. И все, что компилятор будет знать про вызываемую функцию на момент компиляции моего файла — это ее сигнатура: "int f(int)"

Если бы с функциями со статическими переменными компилятор должен был как-то особенно обращаться, у них был бы специальный модификатор, аналогично const, чтобы из сигнатуры следовало наличие статических переменных.
Re: std::cout и static variable - порядок вывода в консоль
От: rg45 СССР  
Дата: 05.05.11 11:44
Оценка:
Здравствуйте, Valen, Вы писали:

V>1. Я правильно понял, что строка cout<<f(x1)<<f(x2)<<f(x3) есть UB, т.е. в общем случае не известно в каком именно порядке будет осуществлен вывод?


Немного не так. Порядок, в котором будут выводиться результаты функций f1, f2 и f3 известен — слева направо. А вот в каком порядке эти функции будут вызваны неизвестно — порядок определяет сам компилятор. Судя по выводимым числам (9 7 4), можно сказать, что сначала все функции были вызваны справа налево и получены результаты: — f(3) — > 4, f(2) -> 7, f(1) — 9, а затем уже полученные результаты выведены в поток в последовательности, соответсвующей порядку вывода в выражении: 9, 7, 4.

Неопределенность в порядке вызова функций действительно можно квалифицировать как UB, только UB в этом случае — unspecified behavior, а не undefined.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: std::cout и static variable - порядок вывода в консол
От: Pavel Dvorkin Россия  
Дата: 05.05.11 12:22
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Valen, Вы писали:


V>>1. Я правильно понял, что строка cout<<f(x1)<<f(x2)<<f(x3) есть UB, т.е. в общем случае не известно в каком именно порядке будет осуществлен вывод?


R>Немного не так. Порядок, в котором будут выводиться результаты функций f1, f2 и f3 известен — слева направо. А вот в каком порядке эти функции будут вызваны неизвестно — порядок определяет сам компилятор. Судя по выводимым числам (9 7 4), можно сказать, что сначала все функции были вызваны справа налево и получены результаты: — f(3) — > 4, f(2) -> 7, f(1) — 9, а затем уже полученные результаты выведены в поток в последовательности, соответсвующей порядку вывода в выражении: 9, 7, 4.


R>Неопределенность в порядке вызова функций действительно можно квалифицировать как UB, только UB в этом случае — unspecified behavior, а не undefined.


Хм... Может, я что-то неверно понимаю, но вроде как для вычисления f3 (члена класса или друга) надо бы определить сам член или передаваемый в друга экземпляр. А для этого вроде как нужно вычислить f(x2), так как именно она возвращает этот самый экземпляр (ссылку, конечно). А для вычисления f2 надо по той же причине вычислить f1.

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

int s = x.f(a) + x.g(b);

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

(((cout.f(x1)).f(x2)).f(x3)

Я неправ ?
With best regards
Pavel Dvorkin
Re[3]: std::cout и static variable - порядок вывода в консол
От: rg45 СССР  
Дата: 05.05.11 12:50
Оценка: 1 (1) +2
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Хм... Может, я что-то неверно понимаю, но вроде как для вычисления f3 (члена класса или друга) надо бы определить сам член или передаваемый в друга экземпляр. А для этого вроде как нужно вычислить f(x2), так как именно она возвращает этот самый экземпляр (ссылку, конечно). А для вычисления f2 надо по той же причине вычислить f1.


PD>Порядок вычисления функций компилятор может определять, если их можно вызвать в произвольном порядке, например


PD>int s = x.f(a) + x.g(b);


PD>а тут порядок не произвольный.


PD>(((cout.f(x1)).f(x2)).f(x3)


PD>Я неправ ?



Чуть по-другому. В данном случае operator<< — это не функция член, а свободная функция. Поэтому выражение:
cout << f(1) <<f(2) <<f(3);

эквивалентно следующему:
operator<< (operator<< (operator<< (cout, f(1)), f(2)), f(3));

На самом верхнем уровне это выражение — это вызов свободной функции, которая принимает два фактических параметра:
  1. operator<< (operator<< (cout, f(1)), f(2))
  2. f(3)

Поскольку порядок вычисления фактических параметров неопределен, компилятор волен вычислить их в любом порядке. В данном случае вначале был вычислен второй аргумент — f(3), а за ним первый — operator<< (operator<< (cout, f(1)), f(2)).
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: std::cout и static variable - порядок вывода в консол
От: Pavel Dvorkin Россия  
Дата: 05.05.11 12:55
Оценка:
Здравствуйте, rg45, Вы писали:

R>Чуть по-другому. В данном случае operator<< — это не функция член, а свободная функция. Поэтому выражение:


Да, верно. Но тогда получается, что для случая функции-члена этот порядок определен, а для друга нет.
With best regards
Pavel Dvorkin
Re[5]: std::cout и static variable - порядок вывода в консол
От: rg45 СССР  
Дата: 05.05.11 13:08
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

R>>Чуть по-другому. В данном случае operator<< — это не функция член, а свободная функция. Поэтому выражение:


PD>Да, верно. Но тогда получается, что для случая функции-члена этот порядок определен, а для друга нет.


Гм... не уверен. Давайте посмотрим, что происходит, при разборе компилятором выражения:
a.foo(bar, baz);

В этом случае выражение a является таким же фактическим параметром функции foo, как и выражения bar и baz, и порядок их вычисления не определен. Другое дело, если бы оператор "точка" порождал бы в полном выражении точку следования. Тогда компилятор был бы обязан вычислить выражение a первым. Но по стандарту оператор "точка" не является точкой следования, поэтому функции-члены в плане порядка вычисления фактических аргументов (включая выражение, вычисляющее экземпляр объекта) ничем не отличаются от свободных функций.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: std::cout и static variable - порядок вывода в консол
От: rg45 СССР  
Дата: 05.05.11 13:38
Оценка:
Здравствуйте, rg45, Вы писали:

R>...поэтому функции-члены в плане порядка вычисления фактических аргументов (включая выражение, вычисляющее экземпляр объекта) ничем не отличаются от свободных функций.


Вот иллюстрация того, что выражение, вычисляющее this вовсе не обязательно вычисляется первым: http://codepad.org/TngFTgg7
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: [offtop] Дополнительный экзаменационный вопрос :)
От: rg45 СССР  
Дата: 05.05.11 14:12
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Да, верно. Но тогда получается, что для случая функции-члена этот порядок определен, а для друга нет.


Что будет выведено в результате работы следующей программы:
#include <iostream>

struct A
{
  explicit A(int x) { std::cout << x; }
  void foo(const A&) const { }
};

int main()
{
  A(1).foo(A(2));
}

Варианты ответов:
  1. "12"
  2. "21"
  3. "12" или "21"
  4. нет правильного ответа

Правильный ответ — c. Если хочешь завалить, варианты ответов не давай
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: [offtop] Дополнительный экзаменационный вопрос :)
От: Pavel Dvorkin Россия  
Дата: 05.05.11 14:36
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Да, верно. Но тогда получается, что для случая функции-члена этот порядок определен, а для друга нет.


R>Что будет выведено в результате работы следующей программы:

R>
R>#include <iostream>

R>struct A
R>{
R>  explicit A(int x) { std::cout << x; }
R>  void foo(const A&) const { }
R>};

R>int main()
R>{
R>  A(1).foo(A(2));
R>}
R>

R>Варианты ответов:
R>

    R>
  1. "12"
    R>
  2. "21"
    R>
  3. "12" или "21"
    R>
  4. нет правильного ответа
    R>

R>Правильный ответ — c. Если хочешь завалить, варианты ответов не давай


Я курс по С++ не веду

Но вообще как-то странно. Тогда и вычисление индекса под это подходит — это тоже функция, operator[](int index).

a[1].f(a[0]) === f(&a[1]/*this*/,a[0]) // синтаксис нарушил, конечно

и получается, что a[1] и a[0] можно вычислять в любом порядке ?

Но тогда здесь UB.

i = 1;
a[i].f(a[i--])
With best regards
Pavel Dvorkin
Re[7]: [offtop] Дополнительный экзаменационный вопрос :)
От: rg45 СССР  
Дата: 05.05.11 14:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Но вообще как-то странно. Тогда и вычисление индекса под это подходит — это тоже функция, operator[](int index).


PD>a[1].f(a[0]) === f(&a[1]/*this*/,a[0]) // синтаксис нарушил, конечно


PD>и получается, что a[1] и a[0] можно вычислять в любом порядке ?


PD>Но тогда здесь UB.


PD>i = 1;

PD>a[i].f(a[i--])

Именно так. Только UB — не undefined, а unspecified — программа вцелом well-formed, если нет вылета за диапазон, конечно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: [offtop] Дополнительный экзаменационный вопрос :)
От: Pavel Dvorkin Россия  
Дата: 05.05.11 15:16
Оценка: +1 :)
Здравствуйте, rg45, Вы писали:

PD>>Но тогда здесь UB.


PD>>i = 1;

PD>>a[i].f(a[i--])

R>Именно так. Только UB — не undefined, а unspecified — программа вцелом well-formed, если нет вылета за диапазон, конечно.


Один черт. Если я не знаю, для какого a[i] будет вызвана f, такой код на свалку.
With best regards
Pavel Dvorkin
Re[7]: [offtop] Дополнительный экзаменационный вопрос :)
От: const_volatile  
Дата: 05.05.11 15:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Но тогда здесь UB.


PD>i = 1;

PD>a[i].f(a[i--])

здесь UB не поэтому. это классическая ошибка с повторным использованием значения переменной и изменения этого значения в одном выражении. то, что тут используется доступ к функции-члену, непринциально. то же самое неопределенное поведение будет в выражении "a[i] = a[i--];" -- момент постдекремента в выражении без точки следования неопределён. этим граблям столько же лет, сколько языку Си, странно что вы не в курсе.
Re[8]: [offtop] Дополнительный экзаменационный вопрос :)
От: Pavel Dvorkin Россия  
Дата: 05.05.11 16:08
Оценка: +1
Здравствуйте, const_volatile, Вы писали:

_>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Но тогда здесь UB.


PD>>i = 1;

PD>>a[i].f(a[i--])

_>здесь UB не поэтому. это классическая ошибка с повторным использованием значения переменной и изменения этого значения в одном выражении. то, что тут используется доступ к функции-члену, непринциально. то же самое неопределенное поведение будет в выражении "a[i] = a[i--];" -- момент постдекремента в выражении без точки следования неопределён. этим граблям столько же лет, сколько языку Си, странно что вы не в курсе.


Я просто от таких конструкций всегда держусь подальше.
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.