Порядок выполнения С++ в цепочке методов
Вывод этой программы:
#include <iostream>
class c1
{
public:
c1& meth1(int* ar) {
std::cout << "method 1" << std::endl;
*ar = 1;
return *this;
}
void meth2(int ar)
{
std::cout << "method 2:"<< ar << std::endl;
}
};
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu).meth2(nu);
}
Есть:
method 1
method 2:0
Почему nu
не 1 при запуске meth2()
?
Ответы
Ответ 1
Поскольку порядок оценки не указан.
Вы видите nu
в main
, который оценивается до 0
, прежде чем вызывается даже meth1
. Это проблема с цепочкой. Я советую не делать этого.
Просто сделайте приятную, простую, понятную, легко читаемую и понятную программу:
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu);
c.meth2(nu);
}
Ответ 2
Я думаю, что эта часть проекта стандарта относительно порядка оценки актуальна:
1.9 Выполнение программы
...
- За исключением случаев, когда отмечено, оценки операндов отдельных операторов и подвыражений отдельных выражения не подвержены влиянию. Вычисления значений операндов оператор упорядочивается перед вычислением значения результата оператора. Если побочный эффект скаляра объект не влияет на какой-либо другой побочный эффект на один и тот же скалярный объект или вычисление значения используя значение одного и того же скалярного объекта, и они не являются потенциально параллельными, поведение undefined
а также:
5.2.2 Вызов функции
...
- [Примечание: Оценки постфиксного выражения и аргументов всенепоследованы относительно одного другой. Все побочные эффекты оценок аргументов секвенированы до ввода функции - примечание конца)
Итак, для вашей строки c.meth1(&nu).meth2(nu);
рассмотрим, что происходит в операторе в терминах оператора вызова функции для окончательного вызова meth2
, поэтому мы четко видим разбивку на постфиксное выражение и аргумент nu
:
operator()(c.meth1(&nu).meth2, nu);
Оценки постфиксного выражения и аргумента для окончательного вызова функции (т.е. постфиксное выражение c.meth1(&nu).meth2
и nu
) не имеют последовательности относительно друг друга в соответствии с правилом вызова функции выше. Поэтому побочный эффект вычисления постфиксного выражения на скалярном объекте ar
не зависит от оценки аргумента nu
перед вызовом функции meth2
. По правилу выполнения программы выше, это поведение undefined.
Другими словами, компилятор не должен оценивать аргумент nu
при вызове meth2
после вызова meth1
- он свободен предположить, что побочные эффекты meth1
не влияют на nu
оценка.
Код сборки, созданный выше, содержит следующую последовательность в функции main
:
- Переменная
nu
выделяется в стеке и инициализируется 0.
- Регистр (
ebx
в моем случае) получает копию значения nu
- Адреса
nu
и c
загружаются в регистры параметров
-
meth1
называется
- Регистр возвращаемого значения и ранее кэшированное значение
nu
в регистре ebx
загружаются в регистры параметров
-
meth2
называется
Критически, на шаге 5 выше компилятор позволяет кэшировать значение nu
с шага 2 для повторного использования в вызове функции meth2
. Здесь он игнорирует возможность того, что nu
может быть изменен вызовом meth1
- 'undefined поведения "в действии.
ПРИМЕЧАНИЕ: Этот ответ по существу изменился из его первоначальной формы. Мое первоначальное объяснение с точки зрения побочных эффектов вычисления операндов, которые не были секвенированы до окончательного вызова функции, были неправильными, потому что они есть. Проблема заключается в том, что вычисление самих операндов неопределенно секвенировано.
Ответ 3
В стандарте С++ 1998 года, раздел 5, абзац 4
За исключением тех случаев, когда отмечено, порядок оценки операндов отдельных операторов и подвыражений отдельных выражений и порядок, в котором происходят побочные эффекты, не определены. Между предыдущими и следующая точка последовательности скалярный объект должен иметь значение, которое его хранимое значение изменялось не более одного раза путем оценки выражения. Кроме того, к предыдущему значению следует обращаться только для определения значения, которое необходимо сохранить. Требования настоящего параграфа удовлетворяются для каждого допустимого упорядочения подвыражений полного выражения; в противном случае поведение undefined.
(Я пропустил ссылку на сноску № 53, которая не имеет отношения к этому вопросу).
По существу, &nu
должен быть оценен перед вызовом c1::meth1()
, а nu
должен быть оценен перед вызовом c1::meth2()
. Однако не требуется, чтобы nu
оценивался до &nu
(например, разрешено сначала оценивать nu
, затем &nu
, а затем вызывается c1::meth1()
- что может быть вашим компилятором делать). Следовательно, выражение *ar = 1
в c1::meth1()
не гарантируется, чтобы быть оцененным до того, как nu
в main()
будет оценено, чтобы передать его в c1::meth2()
.
Более поздние стандарты С++ (которых я не имею на ПК, которые я использую сегодня) имеют по существу одно и то же предложение.
Ответ 4
Я думаю, что при компиляции, прежде чем функции meth1 и meth2 действительно вызываются, параметрам передают их. Я имею в виду, когда вы используете "c.meth1 (& nu).meth2 (nu);" значение nu = 0 было передано в meth2, поэтому не имеет значения, что "nu" изменено последним.
вы можете попробовать следующее:
#include <iostream>
class c1
{
public:
c1& meth1(int* ar) {
std::cout << "method 1" << std::endl;
*ar = 1;
return *this;
}
void meth2(int* ar)
{
std::cout << "method 2:" << *ar << std::endl;
}
};
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu).meth2(&nu);
getchar();
}
он получит ответ, который вы хотите