このプログラムの出力:
_#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
_
meth2()
の開始時にnu
が1ではないのはなぜですか?
評価順序が指定されていないため。
nu
内のmain
が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
)の値は相互に関連して順序付けられていません上記のfunction callルールに従って。したがって、スカラーオブジェクトar
に対する後置式の計算の副作用は、meth2
関数呼び出しの前のnu
の引数評価に関連して順序付けられません。 。上記のプログラム実行ルールにより、これは未定義の動作です。
言い換えると、コンパイラがmeth2
呼び出しの後にmeth1
呼び出しに対するnu
引数を評価する必要はありません-meth1
の副作用がnu
評価に影響しないと仮定することは自由です。
上記によって生成されたアセンブリコードには、main
関数に次のシーケンスが含まれています。
nu
はスタックに割り当てられ、0で初期化されます。ebx
)はnu
の値のコピーを受け取りますnu
およびc
のアドレスがパラメーターレジスタにロードされます。meth1
が呼び出されますnu
レジスタのebx
の-以前にキャッシュされた値がパラメータレジスタにロードされます。meth2
が呼び出されます重大なことに、上記のステップ5で、コンパイラーは、ステップ2のnu
のキャッシュされた値をmeth2
の関数呼び出しで再使用できるようにします。ここでは、nu
がmeth1
の呼び出しによって変更された可能性を無視しています-'undefined behaviour' in action。
注:この回答は実質的に元の形式から変更されています。オペランド計算の副作用に関する最初の説明は、最後の関数呼び出しの前に順序付けされていないため、正しくありませんでした。問題は、オペランド自体の計算が不定に順序付けられているという事実です。
1998 C++標準のセクション5、パラ4
特に明記されている場合を除き、個々の演算子のオペランドと個々の式の部分式の評価の順序、および副作用が発生する順序は指定されていません。前のシーケンスポイントと次のシーケンスポイントの間で、スカラーオブジェクトは、式の評価によって、格納された値を最大で1回変更します。さらに、保存する値を決定するためにのみ、以前の値にアクセスします。この段落の要件は、完全な式の部分式の許容される順序ごとに満たされるものとします。それ以外の場合、動作は未定義です。
(この質問に関係のない脚注#53への参照は省略しました)。
基本的に、&nu
はc1::meth1()
を呼び出す前に評価する必要があり、nu
はc1::meth2()
を呼び出す前に評価する必要があります。ただし、nu
を&nu
の前に評価する必要はありません(たとえば、nu
を最初に評価し、次に&nu
、次にc1::meth1()
を評価することは許可されますと呼ばれます-これはコンパイラが行っていることかもしれません)。したがって、c1::meth1()
の式*ar = 1
は、main()
に渡されるために、c1::meth2()
のnu
が評価される前に評価されることは保証されません。
後のC++標準(今夜使用しているPCには現在ありません)には、基本的に同じ条項があります。
コンパイル時に、関数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();
}
それはあなたが望む答えを得るでしょう