Предположим, следующий фрагмент кода:
#include <iostream>
using namespace std;
char one()
{
cout << "one\n";
return '1';
}
char two()
{
cout << "two\n";
return '2';
}
int main(int,char**)
{
// 1:
cout << one()
<< '\n'
<< two()
<< '\n';
// 2:
operator<<(
operator<<(
operator<<(
operator<<(
cout,
one()),
'\n'),
two()),
'\n');
}
выполнение строк, помеченных как 1
а также 2
будучи скомпилированным с ideone делает то же самое, он печатает следующее:
two
one
1
2
С моей точки зрения, здесь мы наблюдаем неопределенное поведение, поскольку порядок, в котором разрешаются аргументы функции, не определен.
Это был вопрос на собеседовании, печатание выше заданной последовательности (без каких-либо альтернатив) должно было быть правильным ответом, но действительно ли это правильно?
Вы правы, и интервьюер показывает пугающе распространенное отсутствие понимания языка и его правил.
Эти две строки строго эквивалентны, если каждый operator<<
Для первой строки вызывается всегда свободная функция (стандарт гласит, что они есть).
Как вы правильно подумали, порядок между вызовами функций, за исключением случаев, когда одни аргументы являются возвращаемым значением другого, определяется неопределенным образом (до или после, но не определено, какое):
1.9 Выполнение программы
[…] 15 […] При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанный с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочивается перед выполнением каждого выражения или оператора в теле вызываемая функция. [Примечание: вычисления значений и побочные эффекты, связанные с различными выражениями аргументов, не являются последовательными. —Конечная записка] Каждая оценка в вызывающей функции (включая вызовы других функций), которая иначе специально не упорядочена до или после выполнения тела вызываемой функции, неопределенно последовательность по отношению к выполнению вызываемой функции. 9 Несколько контекстов в C ++ вызывают оценку вызова функции, даже если в модуле трансляции отсутствует соответствующий синтаксис вызова функции. [Пример: оценка нового выражения вызывает одну или несколько функций выделения и конструктора; см. 5.3.4. Для другого примера[intro.execution]
Вызов функции преобразования (12.3.2) может возникнуть в тех случаях, когда синтаксис вызова функции не появляется. — конец примера] Ограничения последовательности при выполнении вызываемой функции (как описано выше) являются признаками вызовов функций, которые оцениваются, независимо от того, каким может быть синтаксис выражения, вызывающего функцию.
Называя все части:
cout << one() // a) execute one() ("one\n")
// b) output the return-value ("1")
<< '\n' // c) output newline ("\n")
<< two() // d) execute two() ("two\n")
// e) output the return-value ("2")
<< '\n'; // f) output newline ("\n")
Порядок ограничений:
a < b < c < e < f
d < e < f
Или другое представление:
a < b < c <
< e < f
d <
Таким образом, все действующие полные заказы:
abcdef "one\n1\ntwo\n2\n"abdcef "one\n1two\n\n2\n"adbcef "one\ntwo\n1\n2\n"dabcef "two\none\n1\n2\n"
Вы правы, но ответ на собеседование неверен.
Согласно абзацу §1.9 / 15 стандарта C ++ 11:
За исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных выражений не являются последовательными.
Например, это то, что производит Clang 3.4:
one
1
two
2