Компиляторы и порядок аргументов оценки в С++

Хорошо, я знаю, что стандарт диктует, что реализация С++ может выбирать, в каком порядке оцениваются аргументы функции, но есть ли какие-либо реализации, которые фактически "используют" это в сценарии, где это фактически повлияет программа?

Классический пример:

int i = 0;
foo(i++, i++);

Примечание. Я не ищу, чтобы кто-то сказал мне, что на заказ оценки нельзя полагаться, я хорошо знаю об этом. Меня интересует только то, действительно ли компиляторы действительно оценивают из ордера слева направо, потому что я предполагаю, что если бы они сделали много плохо написанного кода, он сломался (правильно, но они все равно, вероятно, будут жаловаться).

Ответ 1

Это зависит от типа аргумента, вызова вызова вызываемой функции, архитектуры и компилятора. На x86 соглашение о вызове Pascal оценивает аргументы слева направо, тогда как в соглашении вызова C (__ cdecl), это право налево. Большинство программ, которые запускаются на нескольких платформах, учитывают вызывающие соглашения, чтобы пропускать сюрпризы.

Если вам интересно, есть хорошая статья в блоге Raymond Chen. Вы также можете взглянуть на раздел Stack and Calling в руководстве GCC.

Изменить: Пока мы раскалываем волосы: Мой ответ рассматривает это не как вопрос языка, а как платформенный. Стандарт языка не гарантирует или не предпочитает один за другим и оставляет его как неуказанным. Обратите внимание на формулировку. Он не говорит, что это undefined. Ненаказанный в этом смысле означает то, на что вы не можете рассчитывать, не переносимое поведение. У меня нет C spec/draft, но он должен быть похож на мой проект n2798 (С++)

Некоторые другие аспекты и операции абстрактной машины описаны в этом Международном стандарте как неуказанные (например, порядок оценки аргументов функции). Там, где это возможно, настоящий международный стандарт определяет набор допустимых видов поведения. Они определяют недетерминированные аспекты абстрактной машины. Таким образом, экземпляр абстрактной машины может иметь более одной возможной последовательности выполнения для данной программы и заданного ввода.

Ответ 2

Я нашел ответ в стандартах С++.

Пункт 5.2.2.8:

Порядок оценки аргументов неуточнен. Все побочные эффекты оценок выражения аргументов вступают в силу до ввода функции. Порядок оценки постфиксного выражения и списка выражений аргумента не определено.

Другими словами, это зависит только от компилятора.

Ответ 3

Прочтите это

Это не точная копия вашего вопроса, но мой ответ (и несколько других) также охватывает ваш вопрос.

Есть очень хорошие причины оптимизации, из-за которых компилятор может не просто выбирать справа налево, но также чередовать их.

Стандарт даже не гарантирует последовательный заказ. Это гарантирует только то, что при вызове функции все аргументы были полностью оценены.

И да, я видел несколько версий GCC именно так. Для вашего примера будет вызван foo (0,0), после чего мне будет 2. (Я не могу дать вам точный номер версии компилятора. Это было давно, но я не удивлюсь, если это повторится снова. Это эффективный способ запланировать инструкции)

Ответ 4

Все аргументы оцениваются. Заказ не определен (согласно стандарту). Но все реализации C/С++ (которые я знаю) оценивают аргументы функции от справа налево. EDIT: CLang является исключением (см. Комментарий ниже).

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

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

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

Теперь, порядок слева направо налево не предусматривает правостороннего порядка оценки, но в ранних компиляторах памяти недостаточно. В порядке оценки справа налево один и тот же стек можно использовать на месте (по существу, после оценки аргумента, который может быть выражением или вызовом funciton! - возвращаемое значение уже находится в правильной позиции на стек). При оценке слева направо значения аргументов должны храниться отдельно и отбрасываться обратно в стек в обратном порядке.

Ответ 5

Я ожидаю, что большинство современных компиляторов будут пытаться чередовать инструкции, вычисляющие аргументы, учитывая, что они необходимы стандарту С++, чтобы быть независимыми и, следовательно, не имеют каких-либо взаимозависимостей. Это должно помочь сохранить полностью сконфигурированные блоки исполнения ЦП и тем самым увеличить пропускную способность. (По крайней мере, я ожидал бы, что компилятор, который утверждает, что является оптимизирующим компилятором, сделает это, когда будут указаны флаги оптимизации.)

Ответ 6

В прошлый раз я видел различия между VS2005 и GCC 3.x на оборудовании x86 в 2007 году. Так что это была (была?) Очень вероятная ситуация. Поэтому я никогда больше не полагаюсь на оценочный порядок. Возможно, сейчас это лучше.