CおよびC ++での共用体の目的


254

私は以前にユニオンを快適に使用しました。今日、私はこの投稿を読んで、このコードが

union ARGB
{
    uint32_t colour;

    struct componentsTag
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    } components;

} pixel;

pixel.colour = 0xff040201;  // ARGB::colour is the active member from now on

// somewhere down the line, without any edit to pixel

if(pixel.components.a)      // accessing the non-active member ARGB::components

実際には未定義の動作です。つまり、最近書き込まれたもの以外のユニオンのメンバーから読み取ると、未定義の動作になります。これがユニオンの使用目的ではない場合、何ですか?入念に説明してもらえますか?

更新:

後からいくつかのことを明確にしたいと思いました。

  • 質問に対する答えは、CとC ++で同じではありません。私の無知な若い自己はそれをCとC ++の両方としてタグ付けしました。
  • C ++ 11の標準を精査した後、非アクティブな共用体メンバーへのアクセス/検査が未定義/未指定/実装定義であると断言することはできませんでした。私が見つけたすべては§9.5/ 1でした:

    標準レイアウト共用体に共通の初期シーケンスを共有する複数の標準レイアウト構造体が含まれ、この標準レイアウト共用体タイプのオブジェクトに標準レイアウト構造体の1つが含まれている場合、任意の共通初期シーケンスを検査することが許可されています標準レイアウトの構造体メンバーの。§9.2/ 19:対応するメンバーにレイアウト互換のタイプがあり、どちらのメンバーもビットフィールドではないか、1つ以上の初期シーケンスの幅が同じビットフィールドである場合、2つの標準レイアウト構造体は共通の初期シーケンスを共有しますメンバー。

  • Cにいる間(C99 TC3-DR 283以降)、これを行うことは合法です(これをもたらしたPascal Cuoqに感謝します)。ただし、読み取った値が読み取られた型に対して無効である(いわゆる「トラップ表現」)場合、未定義の動作が発生する可能性があります。それ以外の場合、読み取られる値は実装定義です。
  • C89 / 90はこれを不特定の動作(Annex J)の下で呼びかけ、K&Rの本はその実装は定義されていると述べています。K&Rからの引用:

    これが共用体の目的です-いくつかのタイプのいずれかを合法的に保持できる単一の変数。[...]使用法に一貫性がある限り:取得したタイプは、最後に保存されたタイプでなければなりません。ユニオンに現在格納されているタイプを追跡するのはプログラマの責任です。何かが1つのタイプとして格納され、別のタイプとして抽出される場合、結果は実装に依存します。

  • StroustrupのTC ++ PLからの抜粋(私の強調)

    ユニオンの使用は、データの互換性のために不可欠です[...] 「型変換」に誤用されることがあります。

何よりも、この質問(私の質問以来、タイトルは変更されていません)は、共用体の目的を理解することを意図して提起されました。たとえば、コードの再利用のための継承の使用はもちろんC ++標準で許可されていますが、C ++言語の機能として継承を導入する目的や本来の意図ではありません。これが、アンドレイの答えが受け入れられたままである理由です。


11
簡単に言うと、コンパイラーは構造体の要素間にパディングを挿入できます。従って、b, g, r,且つa連続でなくてもよいので、レイアウトが一致しませんuint32_t。これは、他の人が指摘したエンディアネスの問題に追加されます。
Thomas Matthews

8
これがまさに、質問CとC ++にタグを付けるべきではない理由です。答えは異なりますが、回答者は自分が回答しているタグを知らないため(ご存じですか?)、ごみが出ます。
Pascal Cuoq 2013

5
@downvoter説明しなくてありがとう、私はあなたが私にあなたの不満を魔法のように理解し、将来それを繰り返さないようにしたいことを理解しています:P
legends2k

1
ユニオンを設立するという当初の意図については、C規格ではCユニオンの日付が数年後になることに注意してください。Unix V7をざっと見てみると、共用体によるいくつかの型変換が示されています。
ninjalj 2015

3
scouring C++11's standard I couldn't conclusively say that it calls out accessing/inspecting a non-active union member is undefined [...] All I could find was §9.5/1...本当に?段落の冒頭の主なポイントではなく、例外の注記を引用している場合:「ユニオンでは、非静的データメンバーの最大で1つがいつでもアクティブになることができます。つまり、非静的データメンバーはいつでもユニオンに格納できます。」-およびp4まで:「一般的に、明示的なデストラクタ呼び出しと配置演算子を使用して、ユニオンのアクティブなメンバーを変更する必要があります
underscore_d

回答:


407

労働組合の目的はかなり明白ですが、なぜか人々はそれをかなり頻繁に見逃します。

unionの目的は、同じメモリ領域を使用して異なる時間に異なるオブジェクトを格納することでメモリを節約することです。それでおしまい。

ホテルの部屋のようなものです。さまざまな人々が重なり合っていない期間住んでいます。これらの人々は会うことはなく、一般にお互いについて何も知りません。部屋のタイムシェアリングを適切に管理することにより(つまり、異なる人々が1つの部屋に同時に割り当てられないようにすることで)、比較的小さなホテルは、比較的多数の人々に宿泊施設を提供できます。のためです。

それこそ、組合が行うことです。プログラム内のいくつかのオブジェクトが値ライフタイムが重複しない値を保持していることがわかっている場合は、これらのオブジェクトをユニオンに「マージ」して、メモリを節約できます。ホテルの部屋の各瞬間に最大1つの「アクティブな」テナントがあるのと同じように、組合はプログラムの各瞬間に最大で1つの「アクティブな」メンバーを持っています。「アクティブな」メンバーだけが読み取ることができます。他のメンバーに書き込むことにより、「アクティブ」ステータスをその他のメンバーに切り替えます。

なんらかの理由で、このユニオンの本来の目的は、まったく異なるもので「オーバーライド」されました。ユニオンの1つのメンバーを記述し、別のメンバーを通してそれを検査します。この種のメモリの再解釈(別名「タイプパニング」)は、共用体の有効な使用法ではありません。通常、未定義の動作につながり、C89 / 90で実装定義の動作を生成すると説明されています。

編集:タイプのパンニング(つまり、あるメンバーを書き込んでから別のメンバーを読み取る)の目的でユニオンを使用することは、C99標準のテクニカルコリジェンダの1つでより詳細な定義が与えられました(DR#257およびDR#283を参照)。ただし、正式にこれがトラップ表現を読み取ろうとして未定義の動作が発生するのを防ぐことはできないことに注意してください。


37
+1、手の込んだ、簡単で実用的な例、組合の遺産についての発言!
legends2k 2010

6
私がこの回答で抱えている問題は、私が見たほとんどのOSに、これとまったく同じことを行うヘッダーファイルがあることです。たとえば<time.h>、WindowsとUnixの両方の古い(64ビット以前の)バージョンで見ました。これを「無効」および「未定義」として却下することは、この方法で機能するコードを理解するように求められる場合、実際には十分ではありません。
TED

31
@AndreyT「非常に最近まで、タイプパニングにユニオンを使用することは合法ではありませんでした」:2004年は「最近」ではありません。実際には、ユニオンによる型抜きは、C89では合法で、C11では合法であり、委員会が誤った表現を修正し、その後にTC3をリリースするまでに2004年までかかりましたが、C99では合法でした。open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm
Pascal Cuoq 2013

6
@ legends2kプログラミング言語は標準で定義されています。C99標準のTechnical Corrigendum 3は、脚注82でタイプパンニングを明示的に許可しています。これは、ロックスターがインタビューを受け、気候変動に関する彼らの意見を表明するテレビではありません。Stroustrupの見解は、C標準の記述に影響を与えません。
Pascal Cuoq 2013年

6
@ legends2k「個人の意見は問題ではなく、標準のみが問題であることを知っています」コンパイラ作成者の意見は、(非常に貧弱な)言語の「仕様」よりもはるかに重要です。
curiousguy

38

unionを使用して、次のような構造体を作成できます。これには、実際に使用されているunionのコンポーネントを示すフィールドが含まれています。

struct VAROBJECT
{
    enum o_t { Int, Double, String } objectType;

    union
    {
        int intValue;
        double dblValue;
        char *strValue;
    } value;
} object;

未定義の動作の混乱に陥ることなく、おそらくこれは私が考えることができる組合の最も意図された振る舞いであることに完全に同意します。ただ言って、使用しているときには、廃棄物のスペースではないだろうintか、char*対象の10の項目について] [; その場合、VAROBJECTの代わりに各データ型に対して個別の構造体を実際に宣言できますか?それは乱雑さを減らし、より少ないスペースを使用しませんか?
legends2k 2010

3
凡例:場合によっては、それができないこともあります。JavaでObjectを使用する場合と同じケースで、CでVAROBJECTのようなものを使用します。
Erich Kitzmueller、2010

タグ付き共用体のデータ構造は、説明したように、共用体の唯一の合法的な使用のようです。
legends2k 14年

また、値の使用方法の例も示します。
Ciro Santilli郝海东冠状病六四事件法轮功

1
@CiroSantilli新疆改造中心六四事件法轮功C ++ Primerの例の一部が役立つかもしれません。wandbox.org/permlink/cFSrXyG02vOSdBk2
Rick

34

言語の観点からは、動作は未定義です。プラットフォームが異なれば、メモリの配置やエンディアンの制約も異なる可能性があることを考慮してください。ビッグエンディアンマシンとリトルエンディアンマシンのコードは、構造体の値を異なる方法で更新します。言語の動作を修正するには、すべての実装で同じエンディアン(およびメモリ配置の制約...)を使用して使用を制限する必要があります。

C ++を使用していて(2つのタグを使用している)、移植性を本当に気にしている場合は、構造体を使用して、uint32_tビットマスク操作でフィールドを受け取り、フィールドを適切に設定するセッターを提供できます。Cでも関数を使用して同じことができます。

編集:私はAProgrammerが投票の回答を書き留めて、これを閉じることを期待していました。一部のコメントで指摘されているように、エンディアンは標準のほかの部分では各実装に何をするかを決定させることによって処理され、配置とパディングも異なる方法で処理できます。ここで、AProgrammerが暗黙的に参照する厳密なエイリアシングルールは、ここで重要なポイントです。コンパイラーは、変数の変更(または変更の欠如)を仮定することができます。共用体の場合、コンパイラーは命令を並べ替えて、各色成分の読み取りをcolor変数への書き込みに移動できます。


明確でシンプルな返信の+1!私は、移植性のために、2番目の段落で指定した方法が有効であることに同意します。しかし、私のコードが単一のアーキテクチャに結び付けられている場合(収益性の代償を払って)、質問で述べた方法を使用できます。これは、ピクセル値ごとに4バイトを節約し、その関数の実行にいくらかの時間を節約できるためです。 ?
legends2k 2010

エンディアンの問題は、規格にそれを未定義の動作として宣言することを強制しません。
JoeG 2010

1
@ legends2k、問題は、オプティマイザがuint8_tに書き込むことによってuint32_tが変更されていないと想定する可能性があるため、最適化がその想定を使用するときに誤った値を取得する... @Joe、未定義の動作は、ポインター(私は知っています、いくつかの例外があります)。
AProgrammer 2010

1
@ legends2k / AProgrammer:reinterpret_castの結果は、実装によって定義されます。返されたポインタを使用しても、未定義の動作は発生せず、実装で定義された動作のみが発生します。つまり、動作は一貫性があり、定義されている必要がありますが、移植性はありません。
JoeG 2010

1
@ legends2k:適切なオプティマイザは、バイト全体を選択し、バイトを読み書きするためのコードを生成するビット単位の演算を認識します。例uint8_t getRed()const {return color&0x000000FF; } void setRed(uint8_t r){color =(color&〜0x000000FF)| r; }
Ben Voigt

22

私が定期的に遭遇する最も一般的な使用法unionエイリアシングです。

以下を検討してください。

union Vector3f
{
  struct{ float x,y,z ; } ;
  float elts[3];
}

これは何をしますか?次のいずれかの名前で、Vector3f vec;のメンバーにクリーンできちんとしたアクセスを許可します。

vec.x=vec.y=vec.z=1.f ;

または配列への整数アクセス

for( int i = 0 ; i < 3 ; i++ )
  vec.elts[i]=1.f;

場合によっては、名前でアクセスするのが最も明確な方法です。その他の場合、特に軸がプログラムで選択されている場合は、数値インデックスで軸にアクセスする方が簡単です。xの場合は0、yの場合は1、zの場合は2です。


3
これはtype-punning、質問でも言及されています。質問の例も同様の例を示しています。
legends2k 2013

4
それはタイプのパンニングではありません。私の例では、型はmatchなので、「しゃれ」はなく、単にエイリアスです。
bobobobo 2013

3
はい、しかしそれでも、言語標準の絶対的な観点から、書き込まれるメンバーと読み取られるメンバーは異なります。これは、質問で述べたように未定義です。
legends2k

3
将来の標準がこの特定のケースを「共通の初期サブシーケンス」ルールの下で許可されるように修正することを願っています。ただし、配列は現在の表現ではその規則に参加しません。
Ben Voigt 2014

3
@curiousguy:構造体のメンバーが任意のパディングなしで配置されるという要件は明らかにありません。コードが構造体メンバーの配置または構造体サイズをテストする場合、アクセスが共用体を介して直接行われる場合、コードは機能しますが、規格を厳密に読み取ると、共用体または構造体メンバーのアドレスを取得すると、使用できないポインターが生成されることを示します。独自の型のポインタとして。ただし、最初にそれを囲んでいる型または文字型へのポインタに変換する必要があります。リモートで実行可能なコンパイラは、より多くのものを機能させることで言語を拡張します...
supercat '28

10

あなたが言うように、これは厳密に定義されていない動作ですが、多くのプラットフォームで「動作」します。共用体を使用する本当の理由は、バリアントレコードを作成することです。

union A {
   int i;
   double d;
};

A a[10];    // records in "a" can be either ints or doubles 
a[0].i = 42;
a[1].d = 1.23;

もちろん、バリアントが実際に何を含んでいるかを言うために、ある種の弁別子も必要です。また、C ++では、ユニオンはPOD型のみを含むことができるため、あまり使用されないことに注意してください。


このように使用しましたか(質問のように)?? :)
legends2k 2010

それは少し知識が豊富ですが、「バリアントレコード」をまったく受け入れません。つまり、彼らが念頭に置いていたと確信していますが、彼らが優先事項である場合は、なぜ提供しないのですか?「他のものも構築するのに役立つかもしれないので、ビルディングブロックを提供する」は、直感的に可能性が高いように思われます。メモリは、I / Oレジスタがマッピングされ、入力と出力レジスタ(オーバーラップしながら)は自分の名前、種類等と異なる実体である-特に念頭に置いて、おそらくた少なくとも一つ以上のアプリケーション所与
Steve314

@ Stev314それが彼らが考えていた使用であったなら、彼らはそれを未定義の振る舞いではないようにすることができたでしょう。

@ニール:未定義の動作に影響を与えることなく、実際の使用状況について最初に言う+1。他のタイプのパンニング操作(reinterpret_castなど)のように実装を定義した可能性があります。しかし、私が尋ねたように、タイプパンニングにそれを使用しましたか?
legends2k 2010

@Neil-メモリマップされたレジスタの例は未定義ではなく、通常のエンディアンなどは別にして、「揮発性」フラグが与えられています。このモデルのアドレスへの書き込みは、同じアドレスの読み取りと同じレジスタを参照しません。したがって、読み返していないので、「何を読み返していますか」という問題はありません。そのアドレスに書き込んだ出力は、読んだときに独立した入力を読み取っているだけです。唯一の問題は、ユニオンの入力側を読み取り、出力側を書き込むことを確認することです。埋め込まれたもので一般的でした-おそらくまだです。
Steve314、2010

8

Cでは、バリアントのようなものを実装する良い方法でした。

enum possibleTypes{
  eInt,
  eDouble,
  eChar
}


struct Value{

    union Value {
      int iVal_;
      double dval;
      char cVal;
    } value_;
    possibleTypes discriminator_;
} 

switch(val.discriminator_)
{
  case eInt: val.value_.iVal_; break;

litlleメモリの場合、この構造はすべてのメンバーを持つ構造体よりも少ないメモリを使用します。

ちなみにCは

    typedef struct {
      unsigned int mantissa_low:32;      //mantissa
      unsigned int mantissa_high:20;
      unsigned int exponent:11;         //exponent
      unsigned int sign:1;
    } realVal;

ビット値にアクセスします。


どちらの例も標準で完全に定義されていますが、しかし、ねえ、ビットフィールドを使用すると、移植できないコードが確実に撃たれるのではないでしょうか。
legends2k 2010

いいえ、そうではありません。私の知る限り、広くサポートされています。
トトンガ2010

1
コンパイラのサポートは、移植性のあるものにはなりません。CブックC(したがってC ++)では、マシンワード内のフィールドの順序は保証されないため、後者の理由でフィールドを使用する場合、プログラムは移植不可能であるだけでなく、コンパイラにも依存します。
legends2k

5

これは完全に未定義の動作ですが、実際にはほとんどすべてのコンパイラで動作します。これは非常に広く使用されているパラダイムであり、自尊心のあるコンパイラはこのような場合に「正しいこと」を行う必要があります。これは、一部のコンパイラーで壊れたコードを生成する可能性があるタイプ・パンニングよりも確実に推奨されます。


2
エンディアンの問題はありませんか?「未定義」と比較すると比較的簡単な修正ですが、一部のプロジェクトでは考慮する必要があります。
Steve314

5

C ++では、Boost Variantがunionの安全なバージョンを実装し、未定義の動作をできるだけ防ぐように設計されています。

そのパフォーマンスは構成体と同じですenum + union(スタックも割り当てられます)。ただし、enum:) ではなく型のテンプレートリストを使用します。


5

動作は未定義かもしれませんが、それは単に「標準」がないことを意味します。すべての適切なコンパイラは、パッキングとアラインメントを制御するための#pragmaを提供していますが、デフォルトが異なる場合があります。デフォルトは、使用する最適化設定によっても異なります。

また、ユニオンはスペースを節約するためだけのものではありません。これらは、型パンニングを使用して最新のコンパイラーを支援できます。あなたがreinterpret_cast<>すべての場合、コンパイラはあなたが何をしているのかについて仮定を立てることができません。それはあなたのタイプについて知っていることを捨てて、そして最初からやり直さなければならないかもしれません(CPUクロック速度と比較して最近は非常に非効率的なメモリへのライトバックを強制します)。


4

技術的には未定義ですが、実際にはほとんどの(すべての)コンパイラはreinterpret_cast、ある型から別の型への使用とまったく同じように扱い、その結果、実装が定義されます。私はあなたの現在のコードで眠りを失うことはありません。


" あるタイプから別のタイプへのreinterpret_cast。その結果、実装が定義されます。 "いいえ、そうではありません。実装はそれを定義する必要はなく、ほとんどはそれを定義しません。また、いくつかのランダムな値をポインタにキャストすることで許可される実装定義の動作は何でしょうか?
curiousguy

4

ユニオンの実際の使用のもう1つの例として、CORBAフレームワークは、タグ付きユニオンアプローチを使用してオブジェクトをシリアル化します。すべてのユーザー定義クラスは1つの(巨大な)ユニオンのメンバーであり、整数識別子はデマーシャラーにユニオンの解釈方法を指示します。


4

他の人はアーキテクチャの違いに言及しています(リトル-ビッグエンディアン)。

変数のメモリは共有されているため、1つに書き込むと他の変数が変化し、型によっては値が無意味になるという問題を読みました。

例えば。union {float f; int i; } バツ;

xiへの書き込みは、その後xfから読み取る場合は意味がありません。フロートの符号、指数、または仮数コンポーネントを確認するために意図したものでない限り、

アラインメントの問題もあると思います。いくつかの変数をワードアラインメントにする必要がある場合、期待した結果が得られない可能性があります。

例えば。union {char c [4]; int i; } バツ;

仮に、一部のマシンでcharをワード境界で整列させる必要がある場合、c [0]およびc [1]はiとストレージを共有しますが、c [2]およびc [3]は共有しません。


ワード境界で整列する必要があるバイト?それは意味がありません。バイトは定義上、何のアライメント要件を持っていません。
curiousguy

はい、おそらくもっと良い例を使用するべきでした。ありがとう。
philcolbourn

@curiousguy:バイト配列をワード境界に揃えたい場合が多くあります。たとえば1024バイトの多くの配列があり、頻繁に1つを別の配列にコピーしたい場合、それらをワードアラインメントにすると、多くのシステムで速度が2倍になりmemcpy()ます。一部のシステムは、その理由やその他の理由により、構造体/共用体の外部で発生するchar[]割り当て投機的に調整する場合があります。現存する例では、のiすべての要素が重複するという仮定c[]は移植不可能ですが、それはそのことを保証するものではないためですsizeof(int)==4
スーパーキャット2015年

4

1974年に文書化されたC言語では、すべての構造体メンバーが共通の名前空間を共有し、「ptr-> member」の意味は「ptr」にメンバーの変位を追加し、メンバーのタイプを使用して結果のアドレスにアクセスすることとして定義されました。この設計により、異なる構造定義から取得されたメンバー名で同じオフセットを持つ同じptrを使用できるようになりました。プログラマーは、さまざまな目的でその機能を使用しました。

構造体メンバーに独自の名前空間が割り当てられると、2つの構造体メンバーを同じ変位で宣言することができなくなりました。言語に共用体を追加すると、以前のバージョンの言語で使用可能であったのと同じセマンティクスを実現することが可能になりました(ただし、名前を囲んでいるコンテキストにエクスポートできないため、検索/置換を使用してfoo-> memberを置き換える必要があったかもしれませんfoo-> type1.memberに)。重要なことは、共用体を追加した人々が特定のターゲットの使用法を念頭に置いていることではなく、以前のセマンティクスに依存していたプログラマーが目的を問わず、それを達成できる手段を提供することでした。彼らがそれを行うために異なる構文を使用しなければならなかったとしても、同じ意味論。


しかし、K&Rの本は唯一の「標準」だった、人はそれを使用しないで確認する必要があり、過ぎ去ったのCの時代にはそうではありませんでした、未定義などの標準的な定義などなどで、歴史の授業に感謝どんな目的のためにとUBランドに入ります。
legends2k

2
@ legends2k:標準が作成されたとき、Cの実装の大部分は共用体を同じように扱い、そのような扱いは有用でした。しかし、いくつかはそうではなく、標準の作成者は既存の実装を「不適合」としてブランド化することを嫌がっていました。その代わりに、実装者が何かをするように指示するために標準が必要ない場合は(すでに実行しているという事実から明らかなよう)、仕様を未指定または未定義のままにすると、現状を維持できると考えました。規格が作成される前よりも定義が
緩やかに

2
...もっと最近のイノベーションのようです。これらすべてについて特に悲しいのは、ハイエンドアプリケーションを対象とするコンパイラライターが、1990年代に実装されたほとんどのコンパイラではなく、 "実装の90%、結果は、ハイパーモダンCよりも優れた信頼性の高い言語になるでしょう
スーパーキャット'22

2

ユニオンを使用できる主な理由は2つあります。

  1. あなたの例のように、異なる方法で同じデータにアクセスする便利な方法
  2. 異なるデータメンバーが1つしか「アクティブ」にできない場合にスペースを節約する方法

1ターゲットシステムのメモリアーキテクチャがどのように機能するかを知っていることに基づいて、コードを短く書くためのCスタイルのハックです。すでに述べたように、実際には多くの異なるプラットフォームを対象としない場合は、通常はそれを回避できます。一部のコンパイラでは、パッキングディレクティブも使用できるようになっていると思います(構造体でも使用できます)。

2.の良い例は、COMで広く使用されているVARIANT型にあります。


2

他の人が述べたように、列挙型と結合され、構造体にラップされた共用体は、タグ付き共用体を実装するために使用できます。実用的な使い方の1つは、Rustを実装することです。Rust Result<T, E>は、元々純粋なものを使用して実装されていましたenum(Rustは、列挙型バリアントで追加データを保持できます)。次にC ++の例を示します。

template <typename T, typename E> struct Result {
    public:
    enum class Success : uint8_t { Ok, Err };
    Result(T val) {
        m_success = Success::Ok;
        m_value.ok = val;
    }
    Result(E val) {
        m_success = Success::Err;
        m_value.err = val;
    }
    inline bool operator==(const Result& other) {
        return other.m_success == this->m_success;
    }
    inline bool operator!=(const Result& other) {
        return other.m_success != this->m_success;
    }
    inline T expect(const char* errorMsg) {
        if (m_success == Success::Err) throw errorMsg;
        else return m_value.ok;
    }
    inline bool is_ok() {
        return m_success == Success::Ok;
    }
    inline bool is_err() {
        return m_success == Success::Err;
    }
    inline const T* ok() {
        if (is_ok()) return m_value.ok;
        else return nullptr;
    }
    inline const T* err() {
        if (is_err()) return m_value.err;
        else return nullptr;
    }

    // Other methods from https://doc.rust-lang.org/std/result/enum.Result.html

    private:
    Success m_success;
    union _val_t { T ok; E err; } m_value;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.