Порядок выполнения 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 101

4 ответа:

потому что порядок вычисления не определен.

вы видите nu in main оценивается в 0 прежде чем даже meth1 называется. Это проблема с цепью. Я советую не делать этого.

просто сделайте хорошую, простую, понятную, легко читаемую, легкую для понимания программу:

int main()
{
  c1 c;
  int nu = 0;
  c.meth1(&nu);
  c.meth2(nu);
}

я думаю, что эта часть проект стандарта относительно порядка оценки имеет значение:

1.9 Выполнение Программы

...

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

а также:

5.2.2 вызов функции

...

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

так что для вашей линии 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 функция:

  1. переменная nu выделяется в стека и инициализируется с 0.
  2. регистр (ebx в моем случае) получает копию значения nu
  3. адреса nu и c загружаются в регистры параметр
  4. meth1 называется
  5. регистр возвращаемого значения и ранее кэшированные значения на nu на ebx регистры загружаются в регистры параметров
  6. 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();
}

он получит ответ, который вы хотите