Результат этой программы:
#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()
запуске?
c++
chaining
operator-precedence
Мойзес Виньяс
источник
источник
nu
,&nu
иc
далее в стек в этом порядке, затем вызватьmeth1
, отправить результат в стек, а затем вызватьmeth2
, в то время как соглашение о вызовах на основе регистров захочет загрузитьc
и&nu
в регистры, вызватьmeth1
, загрузитьnu
в регистр, затем вызватьmeth2
.Ответы:
Поскольку порядок оценки не указан.
Вы видите
nu
вmain
настоящее время оценивается в0
еще до того ,meth1
как называется. Это проблема с цепочкой. Советую этого не делать.Просто сделайте красивую, простую, понятную, легкую для чтения и понятную программу:
источник
<<
для вывода и «построители объектов» для сложных объектов со слишком большим количеством аргументов для конструкторов - но это очень плохо сочетается с выходными аргументами.meth1
иmeth2
определен, но оценка параметраmeth2
может произойти раньшеmeth1
...?meth2(meth1(c, &nu), nu)
Я думаю, что эта часть проекта стандарта, касающаяся порядка оценки, актуальна:
а также:
Итак, для вашей строки
c.meth1(&nu).meth2(nu);
рассмотрите, что происходит в operator с точки зрения оператора вызова функции для последнего вызоваmeth2
, чтобы мы четко видели разбивку на постфиксное выражение и аргументnu
:В оценках выражения постфикса и аргумент для вызова функции конечного (то есть выражение постфикса
c.meth1(&nu).meth2
иnu
) являются unsequenced относительно друг друга в соответствии с вызовом функции правило , выше. Следовательно, побочный эффект вычисления постфиксного выражения на скалярном объектеar
не упорядочен по сравнению с оценкой аргументаnu
доmeth2
вызова функции. По приведенному выше правилу выполнения программы это неопределенное поведение.Другими словами, компилятору не требуется оценивать
nu
аргументmeth2
вызова послеmeth1
вызова - он может предполагать отсутствие побочных эффектов,meth1
влияющих наnu
оценку.Ассемблерный код, созданный выше, содержит в функции следующую последовательность
main
:nu
размещается в стеке и инициализируется 0.ebx
в моем случае) получает копию значенияnu
nu
иc
загружаются в регистры параметров.meth1
называетсяnu
вebx
регистре загружаются в регистры параметровmeth2
называетсяВажно отметить, что на шаге 5 выше компилятор позволяет
nu
повторно использовать кэшированное значение из шага 2 в вызове функцииmeth2
. Здесь игнорируется возможность, котораяnu
могла быть изменена призывом кmeth1
«неопределенному поведению» в действии.ПРИМЕЧАНИЕ. Этот ответ по существу изменился по сравнению с исходной формой. Мое первоначальное объяснение с точки зрения побочных эффектов вычисления операндов, которые не были упорядочены до последнего вызова функции, было неверным, потому что они есть. Проблема заключается в том, что вычисление самих операндов имеет неопределенную последовательность.
источник
meth1
он выполняется раньшеmeth2
, но параметр дляmeth2
- это значение,nu
кэшированное в регистр перед вызовом,meth1
то есть компилятор проигнорировал потенциальные побочные эффекты, которые в соответствии с моим ответом.c.meth1(&nu).meth2
) и оценка аргумента этого call (nu
) обычно неупорядочены, но 1) все их побочные эффекты упорядочиваются перед входом вmeth2
и 2) посколькуc.meth1(&nu)
это вызов функции , он имеет неопределенную последовательность с вычислениемnu
. Внутриmeth2
, если он каким-то образом получит указатель на переменную inmain
, он всегда будет видеть 1.meth2
, как указано в пункте 3 цитируемой страницы cppreference (которую вы также не процитировали должным образом).В стандарте C ++ 1998 г., раздел 5, пункт 4
(Я пропустил ссылку на сноску № 53, которая не имеет отношения к этому вопросу).
По сути,
&nu
должен быть оценен перед вызовомc1::meth1()
иnu
должен быть оценен перед вызовомc1::meth2()
. Однако нет никаких требований, которыеnu
оценивались бы раньше&nu
(например, разрешаетсяnu
сначала оценивать, затем&nu
, а затемc1::meth1()
вызывать - что может быть тем, что делает ваш компилятор). Выражение*ar = 1
вc1::meth1()
поэтому не гарантируется быть оценены , прежде чемnu
вmain()
оценивается, чтобы быть переданыc1::meth2()
.В более поздних стандартах C ++ (которых у меня сейчас нет на компьютере, который я использую сегодня) есть, по сути, такой же пункт.
источник
Я думаю, что при компиляции, до того, как на самом деле будут вызваны функции meth1 и meth2, им были переданы параметры. Я имею в виду, когда вы используете "c.meth1 (& nu) .meth2 (nu);" значение nu = 0 было передано в meth2, поэтому не имеет значения, будет ли изменено "nu" позже.
вы можете попробовать это:
он получит ответ, который вы хотите
источник