このトピックは、次のトピックの続編と考えてください。
前回の記事
未定義の動作とシーケンスポイント
これをもう一度見てみましょうfunnyおよびconvoluted式(斜体のフレーズは、上記のトピック* smile *から取られています):
_i += ++i;
_
これにより、未定義の動作が呼び出されます。これを言うとき、i
のtypeは組み込み型の1つであると暗黙的に想定していると思います。
i
のtypeがユーザー定義型である場合はどうなりますか?タイプがIndex
であるとします。これは、この投稿の後半で定義されます(以下を参照)。それでも未定義の動作を呼び出しますか?
はいの場合、なぜですか? i.operator+=(i.operator++());
を書くことと同等ではありませんか、それとも構文的に単純なi.add(i.inc());
ですか?または、それらも未定義の動作を呼び出しますか?
いいえの場合、なぜですか?結局のところ、オブジェクトi
は、連続するシーケンスポイント間で変更twiceされます。経験則を思い出してください: 式はオブジェクトの値を連続する「シーケンスポイント間で1回だけ変更できます 。そして、_i += ++i
_が式の場合、undefined-behaviorを呼び出す必要があります。その場合、次に、同等のi.operator+=(i.operator++());
およびi.add(i.inc());
も、正しくないと思われるundefined-behaviorを呼び出す必要があります!(私が理解している限り)
または、_i += ++i
_は式ではありませんか?もしそうなら、それは何であり、式の定義は何ですか?
それが式であり、同時にその動作がalso正しく定義されている場合、式に関連付けられているシーケンスポイントの数がどういうわけか、式に含まれるオペランドのtypeに依存します。私は(一部でも)正しいですか?
ちなみに、この表現はいかがですか?
_//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
_
応答でもこれを考慮する必要があります(動作が確実にわかっている場合)。 :-)
です
_++++++i;
_
c ++ 03で明確に定義されていますか?結局、これはこれです、
_((i.operator++()).operator++()).operator++();
_
_class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
_
コードのようです
_i.operator+=(i.operator ++());
_
シーケンスポイントに関しては完全に問題なく動作します。 C++ ISO標準のセクション1.9.17は、シーケンスポイントと関数の評価について次のように述べています。
(関数がインラインであるかどうかに関係なく)関数を呼び出す場合、すべての関数引数(存在する場合)の評価後、関数本体の式またはステートメントの実行前に発生するシーケンスポイントがあります。戻り値のコピー後、関数の外で式が実行される前のシーケンスポイントもあります。
これは、たとえば、_operator +=
_へのパラメーターとしてのi.operator ++()
が評価後にシーケンスポイントを持っていることを示します。つまり、オーバーロードされた演算子は関数であるため、通常の順序付けルールが適用されます。
ところで、すばらしい質問です。私は、あなたが私がすでに知っていると思っていた(そして私が知っていると思っていた)言語のすべてのニュアンスを理解するように強制する方法が本当に好きです。 :-)
http://www.eelis.net/C++/analogliterals.xhtml アナログリテラルが思い浮かぶ
unsigned int c = ( o-----o
| !
! !
! !
o-----o ).area;
assert( c == (I-----I) * (I-------I) );
assert( ( o-----o
| !
! !
! !
! !
o-----o ).area == ( o---------o
| !
! !
o---------o ).area );
他の人が言ったように、関数を呼び出しているため、i += ++i
の例はユーザー定義型で機能し、関数はシーケンスポイントを構成します。
その一方で、a[++i] = i
は、a
が基本的な配列型、またはユーザー定義の配列型であることを前提として、それほど幸運ではありません。ここで得られる問題は、i
を含む式のどの部分が最初に評価されるのかわからないことです。 ++i
が評価され、そこにオブジェクトを取得するためにoperator[]
(または未加工のバージョン)に渡され、次にi
の値がその(これは私がインクリメントされた後です)。一方、おそらく後者が最初に評価され、後で割り当てられるように格納されてから、++i
部分が評価されます。
私はそれが明確に定義されていると思います:
C++ドラフト標準(n1905)§1.9/ 16から:
「戻り値のコピー後、関数外の式の実行前にもシーケンスポイントがあります13)。対応する関数呼び出し構文が変換単位に表示されていなくても、C++のいくつかのコンテキストによって関数呼び出しが評価されます。 [例:新しい式の評価により、1つ以上の割り当て関数とコンストラクター関数が呼び出されます。5.3.4を参照してください。別の例については、変換関数の呼び出し( 12.3.2)は、関数呼び出し構文が表示されないコンテキストで発生する可能性があります。—end example]シーケンスはfunction-entryおよびfunction-exit(上記のとおり)は、評価された関数呼び出しの機能です式の構文が何であっても関数を呼び出す可能性があります。 "
私が太字にした部分に注意してください。これは、インクリメント関数呼び出し(i.operator ++()
)の後、複合代入呼び出し(i.operator+=
)。