Порядок выполнения C++ в цепочке методов
вывод этой программы:
#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()
начинается?
4 ответа:
потому что порядок вычисления не определен.
вы видите
nu
inmain
оценивается в0
прежде чем дажеmeth1
называется. Это проблема с цепью. Я советую не делать этого.просто сделайте хорошую, простую, понятную, легко читаемую, легкую для понимания программу:
int main() { c1 c; int nu = 0; c.meth1(&nu); c.meth2(nu); }
я думаю, что эта часть проект стандарта относительно порядка оценки имеет значение:
1.9 Выполнение Программы
...
- за исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных операторов выражения не имеют последовательности. Вычисления значения операнда оператор секвенируется перед вычислением значения результата оператор. если побочный эффект на скалярный объект не имеет последовательности относительно другого побочного эффекта для того же скалярного объекта или вычисления значения используя значение одного и того же скалярного объекта, и они потенциально не совпадают, поведение неопределенный
а также:
5.2.2 вызов функции
...
- [ Примечание: оценок постфиксного выражения и аргументов все неупорядочены относительно одного другой. все побочные эффекты оценки аргументов упорядочиваются до ввода функции-конец Примечание ]
так что для вашей линии
c.meth1(&nu).meth2(nu);
рассмотрим, что происходит в оператора с точки зрения оператора вызова функции для окончательного вызоваmeth2
, поэтому мы ясно видим разбивку на постфиксное выражение и аргументnu
:operator()(c.meth1(&nu).meth2, nu);
в оценки постфиксного выражения и аргументации для последнего вызова функции (т. е. постфиксного выражения
c.meth1(&nu).meth2
иnu
) составляют неупорядоченные относительно друг друга на вызов функции выше правило. Таким образом,побочный эффект вычисления постфиксного выражения на скалярном объектеar
не имеет последовательности относительно оценки аргументаnu
доmeth2
вызов функции. По выполнение программы правило выше, это неопределенное поведение.другими словами, компилятор не должен оценивать до
meth2
звонить послеmeth1
звонок-он свободен принять никакие побочные эффектыmeth1
влияетnu
оценка.код сборки, созданный выше, содержит следующую последовательность в
main
функция:
- переменная
nu
выделяется в стека и инициализируется с 0.- регистр (
ebx
в моем случае) получает копию значенияnu
- адреса
nu
иc
загружаются в регистры параметрmeth1
называется- регистр возвращаемого значения и ранее кэшированные значения на
nu
наebx
регистры загружаются в регистры параметровmeth2
is называетсякритически, в шаге 5 выше компилятор позволяет кэшировать значение
nu
от шага 2 для повторного использования в вызове функции доmeth2
. Здесь он игнорирует возможность того, чтоnu
возможно, был изменен вызовом наmeth1
- 'неопределенное поведение' в действии.Примечание: этот ответ изменился по существу от своей первоначальной формы. Мое первоначальное объяснение в терминах побочных эффектов вычисления операндов не секвенируется перед окончательным вызовом функции были неверны, потому что они есть. Проблема заключается в том, что вычисление самих операндов имеет неопределенную последовательность.
в стандарте C++ 1998 года, Раздел 5, пункт 4
за исключением отмеченных случаев, порядок вычисления операндов отдельных операторов и подвыражений отдельных выражений, а также порядок, в котором имеют место побочные эффекты, не определен. Между предыдущими и следующей точками следования скалярный объект должен иметь свое сохраненное значение изменено не более одного раза при вычислении выражения. Кроме того, предварительное значение должно быть доступно только для определения значение для хранения. Требования настоящего пункта должны выполняться для каждого допустимого порядка подвыражений полного выражения; в противном случае поведение не определено.
(я опустил ссылку на сноску №53, которая не имеет отношения к этому вопросу).
по сути,
&nu
должны быть оценены перед вызовомc1::meth1()
иnu
должны быть оценены перед вызовомc1::meth2()
. Однако нет никакого требования, чтобыnu
быть оценены, прежде чем&nu
(например, допускается, чтоnu
сначала нужно оценить, затем&nu
, а потомc1::meth1()
называется - Что может быть то, что делает ваш компилятор). Выражение*ar = 1
на будет оценено доnu
наmain()
оценивается, чтобы быть переданным вc1::meth2()
.более поздние стандарты C++ (которые у меня в настоящее время нет на ПК, который я использую сегодня вечером) имеют по существу одно и то же предложение.
Я думаю, что при компиляции, прежде чем функции 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(); }
он получит ответ, который вы хотите