web-dev-qa-db-ja.com

C ++でNullableメンバーを表す最良の方法?

可能性のある複製:
C++ではnull可能値

C++でnull可能なメンバーを表す最良の方法は何ですか?

C#では、Nullable<T>タイプを使用できます。すべてが意味のある値を持つことができるわけではないため、このようなデータ型は非常に必要です。 @ Jon Skeet が27ページにまたがる1つの章全体を費やし、優れた本でNullable<T>のみを説明しているほど重要なデータ型です C#in Depth

簡単な例の1つはPersonクラスです。1、次のように定義されます。

struct Person
{
  std::string Name;
  DateTime    Birth;
  DateTime    Death;
  //...
};

人は常に生年月日を持っているので、上記のクラスのBirthメンバーには常にいくつかの意味のある値があります。しかし、Deathはどうですか?その人が生きているなら、それはどのような価値があるでしょうか? C#では、このメンバーはNullable<DataTime>として宣言できます2 これは、人物が生きている場合はnullで割り当てることができます。

C++では、これを解決する最良の方法は何ですか?今のところ、私が考えている解決策は1つだけです。メンバーをpointerとして宣言します。

 DataTime *Death;

これで、人が生きているときの値はnullptrになります。しかし、死者にはnewの使用を強制します。有効な値になるからです。これは、コンパイラによって生成されたdefault copy-semanticコードに依存できないことを意味します。プログラマーは つの規則 (C++ 03)に従ってコピーコンストラクター、コピー割り当て、デストラクターを記述する必要があります。またはC++ 11では 5つの規則 です。

では、この問題を単にpointerにするよりも優れたエレガントな解決策はありますか?


1.他の例には、リレーショナルデータベーステーブルが含まれます。

2.これの省略形もあります。 DataTime?正確にNullable<DateTime>と同じです。

33
Nawaz

あなたは Boost.Optional を調べることができます:

_struct Person
{
  std::string               Name;
  DateTime                  Birth;
  boost::optional<DateTime> Death;
  //...
};
_
  • Deathは最初は「初期化されていません」。
  • 次に、_=_のように、_Death = myDateTime_を使用して値を割り当てることができます。
  • Death.is_initialized()の場合、Death.get()を使用できます。
  • Death.reset()で再度初期化を解除します。

ただし、このような単純なケースでは、通常、たとえばDateTimeの「0000-00-00 00:00:00」のような独自の露骨なセンチネル値を選択する方が一貫性があると考えられます。

DateTimeに依存-@Tomalakが答えで言うように、boost::optional<>は一般的なソリューションです。ただし、たとえば、DateTimeboost::posix_time::ptimeの場合、すでに特別な値がサポートされています(たとえば、not_a_date_timeまたはpos_infin)-これらを使用できます。

6
Nim

私が取り組んだすべてのプロジェクトには、ある種のFallibleMaybe、またはNullableテンプレートクラスがありました。 (実際の名前は、アプリケーションが最初にそれを必要としたものを反映する傾向があります:戻り値としてのFallible、モデルデータベースへのNullableなど)。最近では、Boostがboost::optional;残念ながら、それらはisValid(名前付き)関数の代わりに暗黙の変換を使用します。その結果、コードが著しく読みにくくなります(おそらく、独自のMaybeを実装することを除いて、コードを避けたくなるほどで​​す)。 )。

5
James Kanze

プログラマーの軍団があなたの前にしたことをすることができます! 「存在しない」として特定の値を使用します...たとえば、「2000年1月1日」がかなり一般的であると聞きました:-) :-)相互運用性の理由から、「2038年1月19日03:14:07 UTC」を使用できます。 :-) :-)(それが明確でない場合、それは冗談です。私はY2K問題とY2038問題を参照しています。「特別な」日付を状態として使用する問題を示しています... 11のようなもの-11-11および類似)

DataTimeの最大値/最小値はおそらくより正確です:-)そして、「状態」と「値」を混合しているため、それはまだ間違っています。 C++でNullable型を再構築することをお勧めします(結局、それは非常に簡単です:null/not nullのブール値とTフィールドを持つテンプレートクラス)

1
xanatos

死がいつになる可能性は特に低いため、before birth最初にbirth-1に設定して、実際のイベントで変更することもできます。より一般的な用語では、おそらくbirth-1をセンチネル値またはプレースホルダー値と呼びます。実際の値と間違われないように十分に低い定数値を選択することもできますが、これはデータについてある程度の知識があることを前提としています。

1
mtijn