C++ 11はunion
で標準のレイアウトタイプの使用を許可しました: 組合員はユーザー定義のコンストラクターを持っています
私の質問は次のとおりです。union
がスコープ外になったときに、カスタムデストラクタが呼び出されることは保証されますか?
私が理解していることは、切り替え時に手動で破棄して構築する必要があるということです http://en.cppreference.com/w/cpp/language/union#Explanation
しかし、このような例についてはどうでしょう:
{
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
s
がスコープ外になったとき、string
のデストラクタを呼び出さなかったため、ヒープに割り当てられた文字列をメモリからリークしましたか?
あなたが提供したあなたの例ではstr
は破壊されません。 [class.union]/2の標準状態
共用体は、メンバー関数(コンストラクターとデストラクターを含む)を持つことができますが、仮想(10.3)関数を持つことはできません。労働組合は基本クラスを持たないものとします。共用体は基本クラスとして使用されません。共用体に参照型の非静的データメンバーが含まれている場合、プログラムの形式が正しくありません。ユニオンの最大1つの非静的データメンバーは、中かっこまたは等しい初期化子を持つことができます。 [注:ユニオンの非静的データメンバーに重要なデフォルトコンストラクター(12.1)、コピーコンストラクター(12.8)、移動コンストラクター(12.8)、コピー代入演算子(12.8)がある場合)、移動代入演算子(12.8)、またはデストラクタ(12.4)の場合、ユニオンの対応するメンバー関数はユーザーが指定する必要があります。そうしないと、ユニオンに対して暗黙的に削除されます(8.4.3)。 —終了ノート]
重点鉱山
したがって、str
とvec
の両方に自明ではない特別なメンバー関数があるので、それらを自分でユニオンに提供する必要があります。
空のデストラクタの下にある bogdan's コメントに従って、十分ではないことに注意してください。 [class.union]/8には
[...] Xが共用体である場合、そのバリアントメンバーは非静的データメンバーです; [...]
したがって、この共用体のすべてのメンバーはバリアントです。次に、[class.dtor]/8を見ると、
デストラクタの本体を実行し、本体内に割り当てられた自動オブジェクトを破棄した後、クラスXのデストラクタは、Xの直接の非バリアント非静的データメンバーのデストラクタを呼び出します[...]
そのため、デストラクタはユニオンのメンバーをバリアントとして自動的に破壊しません。
struct TU { int type; union { int i; float f; std::string s; } u; TU(const TU& tu) : type(tu.type) { switch (tu.type) { case TU_STRING: new(&u.s)(tu.u.s); break; case TU_INT: u.i = tu.u.i; break; case TU_FLOAT: u.f = tu.u.f; break; } } ~TU() { if (tu.type == TU_STRING) u.s.~string(); } ... };
これにより、正しいメンバーが破棄されるか、std::variant
またはboost::variant
あなたの例はコンパイルされません。ユニオンには、デフォルトで、デストラクタが削除されています。もちろん、どのデストラクタを呼び出す必要がありますか?確かに両方を呼び出すことはできません。また、実際に構成されたメンバーについての情報はどこにも保存されていません。適切なデストラクタを提供するのはあなた次第です。
コードスニペットをコンパイルしようとしたときのGCCの出力は次のとおりです。
In function ‘int main()’:
error: use of deleted function ‘main()::<anonymous union>::~<constructor>()’
vector<int> vec; } s = { "Hello, world"s };
^
note: ‘main()::<anonymous union>::~<constructor>()’ is implicitly deleted because the default definition would be ill-formed:
union { string str;
^
構造体のオブジェクトのコンストラクターは、自明でない型で常に手動で呼び出す必要があります。
通常、常に明示的にそれらを構築する必要もあります。ここでの割り当てが機能するのは奇妙なようです。
疑問がある場合でも、デストラクタが呼び出された場合はいつでもアセンブリを確認できます。
このコードのアセンブリは、basic_string
コンストラクタを呼び出しますが、デストラクタは呼び出しません。したがって、ここでリークが発生します。
using namespace std;
int main(int argc, char** argv){
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
アセンブリを表示するリンク: https://godbolt.org/g/wKu3vf