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

Мне интересно следующее:

void f(int a, int b) { }

int a(int x) { std::cout << "func a" << std::endl; return 1; }
int b(int x) { std::cout << "func b" << std::endl; return 2; }

int x() { std::cout << "func x" << std::endl; return 3; }
int y() { std::cout << "func y" << std::endl; return 4; }

f(a(x()), b(y()));

После прочтения http://en.cppreference.com/w/cpp/language/eval_order Мне все еще трудно понять, возможен ли следующий порядок оценки:

x()y()a()b()

или что стандарт гарантирует, что a(x()) и b(y()) будут оцениваться как единицы, так сказать.

Другими словами, есть ли возможность распечатать

func x
func y
func a
func b

Выполнение этого теста на GCC 5.4.0 дает мне разум более логичным

func y
func b
func x
func a

но это, конечно, не говорит мне ничего о том, что требует стандарт. Было бы неплохо получить ссылку на стандарт.

Ответы

Ответ 1

В С++ 14 и ранее возможно x -> y -> a -> b. Здесь следующие отношения секвенирования:

  • Звонок на x секвенирован до вызова a.
  • Вызов y выполняется до вызова b.
  • Звонок на a секвенирован до вызова f.
  • Вызов b выполняется до вызова f.

Других ограничений на заказ нет. Если вы хотите применить какой-то определенный порядок, вам придется разбить этот вызов на несколько полных выражений.

В стандарте С++ 14 это намерение разъясняется запиской [expr.call]/8:

[Примечание. Оценки постфиксного выражения и аргументов не зависят от другого. Все побочные эффекты оценки аргументов секвенированы до ввода функции. -end note]

Как отмечено в комментариях, на странице cppreference перечислены еще несколько правил последовательности, отмеченных как "с С++ 17". Это основано на n4606, последнем опубликованном проекте для С++ 17. Таким образом, возможно, что для С++ 17 этот порядок больше не будет разрешен.

Ответ 2

Еще один способ взглянуть на это:

Не было никакой пользы для оценки как x, так и y до начала оценки a или b. Фактически, был бы штраф. Дополнительный промежуточный результат должен быть временно сохранен где-то, где либо потребуется дополнительный стек push/pop, либо потребляет дополнительный регистр CPU (чрезмерное использование которого приведет к дополнительным действиям в стеке). Хотя это может быть мало или вообще не имеет последствий для приведенного вами примера, более сложные случаи выявят неэффективность.

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