デストラクタでの動的キャスト


8

このコードは合法ですか?

class Base1 {
};

class Base2 {
public:
    virtual ~Base2() {
        if (!dynamic_cast<Base1*>(this))
            std::cout << "aaaa" << std::endl;
    }
    Base2() {
    }
};

class MyClass: public Base1, public Base2 {
public:
    MyClass() {
    }
    virtual ~MyClass() {
        std::cout << "bbb" << std::endl;
    }
};

int main() {
    MyClass s;
    return 0;
}

両方のプリントが表示されますが、1つしか表示されません。動的キャストは間違っていると思います。この種のチェックは可能ですか?


チェックしようとしていることを明確にできますか?Base2 は、Base1 持つ派生クラスのベースであるかどうかを知りたいですか?
jtbandes

はい、「this」もbase1の子であるかどうかをBase2でチェックインします
greywolf82

「でも、1つしか表示されないはずです」なぜですか?そしてなぜあなたは合法性について疑問を持っていますか?
idclev 463035818

1
@ greywolf82ああごめんなさい!
idclev 463035818

2
dynamic_castコンストラクタとデストラクタで異なる動作をしませんか?
フランソワアンドリュー

回答:


7

多分私は自分で解決策を見つけました、答えは不可能ではありません:

cppreference.comドキュメントの箇条書き6から:

dynamic_castがコンストラクターまたはデストラクターで(直接的または間接的に)使用され、式が現在構築/破壊されているオブジェクトを参照している場合、そのオブジェクトは最も派生したオブジェクトと見なされます。new-typeがポインターまたはコンストラクター/デストラクター自身のクラスまたはそのベースの1つへの参照でない場合、動作は未定義です。

標準の[class.cdtor] / 6も参照してください。

私はBase2デストラクタでBase1にキャストしているので、この動作は未定義です。


1
[class.cdtor]/6、 参考のために。申し訳ありませんが、5ではありません。C++ 17(ドラフトN4659)では5でしたが、/6今のように見えます。
ChrisMM

@ChrisMMその段落は、この回答で参照されている未定義の動作については言及していないようです。実際、私はこの制限を標準のどこにも見つけることができませんでした。
クルミ

cppreferenceが誤って「new-type」と書いたと思いますが、「表現」であるべきだったのです。リンクされた標準パッセージは、破壊されているオブジェクトを参照する場合、オペランドにはデストラクタのクラスまたはそのベースの(静的)タイプが必要であると述べています。質問のコードthisはデストラクタのタイプなので、UBが適用されているとは思いません。ただし、同じ標準段落では、破壊されているオブジェクトの最も派生したタイプはデストラクタのクラスと見なされるため、他の回答で説明されている動作が確認されます。
クルミ

@walnutは、私の答えではなく、標準からの関連する文章を投稿していました。私はそのUBではないことに同意します。
ChrisMM

3

私は@ j6tの答えに同意しますが、ここでは標準参照を使用した拡張推論を示します。

dynamic_cast構築中および破棄中のオブジェクトの特殊な動作は、C ++ 17標準(最終ドラフト)の[class.cdtor] / 5によって記述され、以前の標準バージョンでも同様に記述されています。

特にそれは言う:

a dynamic_­castがデストラクタ[...]で使用されている場合、[...]のオペランドがdynamic_­cast構築中または破棄中のオブジェクトを参照している場合、このオブジェクトは[の型を持つ最も派生したオブジェクトと見なされます。 ...]デストラクタのクラス。のオペランドがdynamic_­cast[...]破壊下のオブジェクトを参照し、オペランドの静的タイプが[...]デストラクタ自身のクラスまたはそのベースの1つへのポインタまたはオブジェクトでない場合、dynamic_castは未定義の動作。

オペランドは式thisです。デストラクタ自体に表示されるため、デストラクタ自身のクラスへのポインタの型が自明です。

ただし、最初の文は、dynamic_cast*thisタイプの最も派生したオブジェクトであるかのように動作することを示していますBase2ので、へのキャストBase1成功することはできませんがために、Base2由来していないBase1、とdynamic_cast<Base1*>(this)いつもあなたが見ている行動で、その結果、NULLポインタを返します。


cppreference.comは、キャストの宛先タイプがデストラクタのクラスまたはそのベースのタイプではなく、これをオペランドタイプに適用するのではなく、未定義の動作が発生すると述べています。それは間違いだと思います。おそらく、箇条書き6 の「新しいタイプ」の言及は、「表現」、上記の私の解釈と一致します。


2

dynamic_castこのような状況では、明確に定義されています。両方の出力行を観察するのは正しいことです。

のデストラクタがBase2 this派生クラスであると想定するのは誤りです。現時点では、派生クラスの部分は既に破棄されているため、派生クラスにすることはできません。実際には、のデストラクタ時にBase2実行する、で指されるオブジェクトがthis あるだけBase2オブジェクト。ためBase2に関連しないBase1任意の方法で、dynamic_castNULLポインタを返し、条件はそれに応じて入力されます。

編集:標準は言う

a dynamic_­castがコンストラクター[...]またはデストラクタ[...]で使用されている場合、参照のオペランドがdynamic_­cast構築中または破棄中のオブジェクトを参照している場合、このオブジェクトはタイプを持つ最も派生したオブジェクトと見なされます。コンストラクタまたはデストラクタのクラスの。のオペランドがdynamic_­cast構築中または破棄中のオブジェクトを参照し、オペランドの静的型がコンストラクターまたはデストラクタ自身のクラスまたはそのベースの1つへのポインタまたはオブジェクトdynamic_­castでない場合、結果は未定義の動作になります。

オペランド thisは破棄中のオブジェクトを参照しています。したがって、デストラクタのクラス(Base2)は、最も派生したクラスと見なされます。そのため、オブジェクトは宛先の型(Base1*)にまったく関係していません。さらに、オペランドの静的な型thisBase2* constであり、これは明らかにデストラクタ自身のクラスへのポインタです。したがって、未定義の動作に関するルールは適用されません。要約すると、明確に定義された動作があります。


1
これは、(標準から引用して)コードが未定義の動作をしていると述べている他の回答と矛盾しています。あなたはそれを反対するためにいくつかの良い議論が必要です、ただ言う
idclev 463035818

1
@ formerlyknownas_463035818もう1つの答えは、標準ではなく、cppreferenceからの引用です。それは明確ではなかったかもしれません。それが言及する標準的な段落は、cppreferenceと同じことを言っていないようです。
クルミ

@walnut cpprefから引用するように編集されている間、それは標準からの引用だと思いました。とにかく、答えは矛盾しており、1つはそれをバックアップするためのソースを持ち、もう1つはそれを言わないだけです...
idclev 463035818

@walnut自分で確認[class.cdtor]/6し、他の回答で述べたように、cpprefと同じように述べています。デストラクタ自身のクラスまたはそのベースの1つであるdynamic_castは、未定義の動作を引き起こします。」
idclev 463035818

1
標準の解釈を追加しました。
j6t
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.