Порядок выполнения 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 ответа:
потому что порядок вычисления не определен.
вы видите
nuinmainоценивается в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регистры загружаются в регистры параметровmeth2is называетсякритически, в шаге 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(); }он получит ответ, который вы хотите