2016-08-21 14 views
45

std :: tupleのメンバーがどの順序で破棄されるかを示す規則はありますか?例えばC++ std :: tuple破棄の順序

Function1Function2std::tuple<std::unique_ptr<ClassA>, std::unique_ptr<ClassB>>を返す場合、私はClassAのインスタンスによって参照される前に、第2の部材によって参照ClassBのインスタンスが破棄される(Function2の範囲が残っている場合)ことを確認することができ最初のメンバー?クラン3.4

std::tuple< std::unique_ptr<ClassA>, std::unique_ptr<ClassB> > Function1() 
{ 
    std::tuple< std::unique_ptr<ClassA>, std::unique_ptr<ClassB> > garbage; 
    get<0>(garbage).reset(/* ... */); 
    get<1>(garbage).reset(/* ... */); 
    return garbage; 
} 

void Function2() 
{ 
    auto to_be_destroyed = Function1(); 
    // ... do something else 

    // to_be_destroyed leaves scope 
    // Is the instance of ClassB destroyed before the instance of ClassA? 
} 
+2

私はそれがあなたの標準ライブラリでどのように 'std :: tuple'がどのように実装されているかに依存していると推測しています。 – Arunmu

+3

'std :: tuple'の破壊の順序を指定する仕様では何も見つかりません。おそらく、不特定のものとして提出されるべきである。 – 101010

+1

http://stackoverflow.com/a/27663655/576911 –

答えて

33

標準では、std::tupleの破壊の順序は指定されていません。 §20.4.1/ P1があることを指定するという事実:

つの引数を持つタプルのインスタンスは、同じ2つの引数を持つ一対の インスタンスと同様です。ここ

同様同じとして解釈されておらず、結果的にstd::tupleは、その引数の逆破壊の順序を持​​っている必要があることを暗示していません。

再帰的性質がstd::tupleであるとすれば、最も可能性が高いのは、破壊の順序が引数の順番であるということです。

また、私の仮定は、GCC BUG 66699のバグレポートに基づいていますが、上記の私の前提は正当なものです。

つまり、std::tupleの破壊の順序は不明です。

13

私はlibstd ++でstd::tupleの再帰的実装に主に起因する可能性が逆の順序を取得std::pair 2要素std::tupleとgの両方++ 5.3の同じ破壊順序を得ます。

だから、基本的に私がコメントで言ったことは、定義された実装です。 BUGレポートから

:STDのレイアウト以来マーティンSebor

によって

コメント::対メンバーが完全に指定されているので、彼らの初期化と破壊の 順序があります。テスト の出力には、この順序が反映されます。

std:stupleサブオブジェクトの初期化(および破壊)の順序はあまり明確ではありません。特定の注文が必要な場合は、少なくとも の仕様を読んですぐにはわかりません。

のlibstdcとのstd ::タプル++の出力は逆のstd 理由::ペア再帰 継承、保存及び構造に依存している実装は、逆の順序で 要素タプルからです。すなわち、最後の要素を格納している基底クラスは に格納され、最初に構築され、各派生クラスが続きます(それぞれ は最後のN番目の要素を格納します)。

バグレポーターは

1本節に引用されている標準の[セクション20.4.1]から引用することができるクラステンプレートタプルとしてタプル タイプを提供するタプルライブラリを記述する任意の 引数でインスタンス化されます。各テンプレート引数は、タプル内の 要素のタイプを指定します。その結果、タプルは異種であり、固定サイズの値の集合である です。 2つの 引数を持つタプルのインスタンス化は、同じ2つの 引数のを持つペアのインスタンス化に似ています。 20.3を参照してください。リンクバグで行われたこのに対して

引数は次のとおりです。彼らはすべての 詳細で同一である

同様のように説明されている

意味するものではありません。 std :: pairとstd :: tupleはそれぞれ異なる の要件を持つ異なるクラスです。この点で と同じように動作する必要があると思われる場合(つまり、サブオブジェクトに で同じ順序が定義されている場合)、 が保証する特定の語句を指す必要があります。

+3

'std :: pair'要素の破壊の順序が逆になっていることをどうお知りしますか? – deepmax

+2

類似した!= idententical。 – 101010

+0

@deepmax std :: pairのlibcxxとlibstd ++の両方の実装に基づいています。 – Arunmu

52

私はあなたの質問に応じて、むしろ直接的な答えよりも、私が学んだ人生の教訓を提供します:

あなたは、複数の選択肢のために、なぜのための合理的な引数を策定することができる場合その代替案は標準で義務づけられているものでなければなりません。のいずれかが義務付けられていると仮定すべきではありません。

タプルのコンテキストでは、コードを管理する人に親切にしてください。また、タプル要素の破壊順序が他の要素の破壊を潜在的に混乱させないようにしてください。それはちょうど悪です...この事をデバッグする必要がある不運なプログラマーを想像してください。実際には、その貧しい魂は、数年後にあなた自身であるかもしれません。あなたが巧妙な技を背中から忘れてしまったときです。

破壊命令を絶対に使用する必要がある場合は、タプルの要素をデータメンバーとして持つ適切なクラスを使用するだけでよい(デストラクタを書くことができ、何がどのような順序で起こる必要があるかを明確にする) 、または破壊のより明白な制御を容易にする何らかの他の配置であってもよい。

+1

これは本当に重要な教訓です! C++でプログラミングする場合、このヒントは常に開発者の心にあるべきです。 – hbobenicio

+0

私はこれにかなり従っていません。 C++ 11の文字列では、連続した記憶域が必要です。しかし、これを必要としない合理的な議論(C++ 03が書かれたときに使用されたもの)があります。または、その例に異議を申し立てる場合は、標準が合理的にいずれかの方向に進むことができる判断を下した別の箇所を選択します。だから私はC++ 11やC++ 14が強化していることに依存するコードを書いてはいけませんか?あるいは、標準が明確でない場合は、次のプログラマーがあなたのやり方と同じように髪を吐くことに頼らないことを意味しますか? –

+0

意図的に愚かな例の場合、 'std :: vector'は' std :: dynamic_array'と呼ばれ、 'std :: valarray'は' std :: vector'と呼ばれるべき合理的な議論があります。しかし、私は、 'std :: vector'は標準が何と言おうとしているかに基づいてコードを書くことは非常に合理的だと思います。それを仮定せずに使うことはできません。だからあなたのアドバイスがどのような状況に当てはまるのか分かりませんでした。 –