1. ホーム
  2. c++

[解決済み] 未定義の動作とシーケンスポイントの再読み込み

2023-06-10 10:15:59

質問

このトピックは、次のトピックの続編であると考えてください。

前回の続き

未定義の動作とシーケンスポイント

これを再確認してみましょう。 面白い 複雑な という表現があります(斜体のフレーズは上記のトピックから引用しています *smile* )。

i += ++i;

私たちは、これが未定義の振る舞いを呼び出すと言っています。このように言うとき、私たちは暗黙のうちに タイプ i は組み込み型の一つです。

もし i はユーザー定義型ですか?その型が Index で、この記事の後半で定義されているとします(下記参照)。それはまだ未定義の振る舞いを呼び出すでしょうか?

もしそうなら、なぜですか?次のように書くことと同等ではありませんか? i.operator+=(i.operator++()); と書くのと同じか、あるいは構文的にもっと単純な i.add(i.inc()); ? あるいは、それらも未定義の振る舞いを呼び起こすのでしょうか?

もしそうでないなら、なぜそうしないのでしょうか?結局のところ、オブジェクト i が変更されるからです。 2回 となります。経験則を思い出してください。 式がオブジェクトの値を変更できるのは、連続したシーケンスポイントの間で一度だけです。 . そして、もし i += ++i が式であるならば,それは未定義の振る舞いを呼び出さなければならない。もしそうであれば、その等価物である i.operator+=(i.operator++());i.add(i.inc()); はまた,undefined-behaviorを呼び出す必要があり,それは真実ではないようです! (私が理解する限り)

あるいは i += ++i 表現 で始まるのでしょうか?もしそうなら、それは何なのか、そして、その定義とは ?

もしそれが式であり、同時にその動作が がよく定義されているのであれば、ある式に関連する配列点の数が何らかの形で、その式に依存することを意味します。 に依存していることを意味します。私は正しいですか(部分的にでも)?


ところで、こんな表現はいかがでしょうか?

//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 では、シーケンス ポイントと関数評価についてこのように説明されています。

関数を呼び出す場合 (関数がインラインであるかどうかにかかわらず)、すべての関数引数の評価後にシーケンス ポイントがあり、関数本体内の式やステートメントの実行前に行われます。また、戻り値のコピーの後、関数外の式の実行の前にもシーケンスポイントがあります。

これは、例えば i.operator ++() をパラメータとして operator += はその評価の後にシーケンスポイントがあります。 つまり、オーバーロードされた演算子は関数であるため、通常のシーケンスルールが適用されます。

ところで、素晴らしい質問ですね。 すでに知っていると思っていた(と思っていた)言語のすべてのニュアンスを理解するよう私に強制しているところが本当に好きです :-)