33

Possible Duplicate:
Nullable values in C++

What is the best way to represent nullable member in C++?

In C#, we can use Nullable<T> type. Such a data type is very much needed as not everything can have meaningful value. It is so important data type that @Jon Skeet has spent one entire chapter, spanned over 27 pages, describing only Nullable<T> in his outstanding book C# in Depth.

One simple example can be a Person class1, defined as:

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

As a person always have birthdate, so the Birth member of the above class will always have some meaningful value. But how about Death? What should it value be if the person is alive? In C#, this member can be declared as Nullable<DataTime>2 which can be assigned with null if the person is alive.

In C++, what is the best way to solve this? As of now, I've only one solution in mind: declare the member as pointer:

 DataTime *Death;

Now its value can be nullptr when the person is alive. But it forces the use of new for dead person, as it's going to have some valid value. It in turn implies one cannot rely on the default copy-semantic code generated by the compiler. The programmer has to write copy-constructor, copy-assignment, destructor following rule of three (C++03), Or in C++11, rule of five.

So do we have any better, elegant solution to this problem than just making it pointer?


1. Other examples include relational database tables, as in many DBMSs columns can be nullable.

2. There is also a shorthand for this. One can write DataTime? which is exactly same as Nullable<DateTime>.

Community
  • 1
  • 1
Nawaz
  • 327,095
  • 105
  • 629
  • 812
  • @TomalakGeret'kal: I think, you should close other topic, as this topic has *more* and better answers. :D.. and I believe, I've described the problem in detail, at conceptual level. – Nawaz Oct 04 '11 at 09:22
  • 1
    You can't just trundle around Stack Overflow posting duplicates and trying to get the originals closed because you prefer your own. – Lightness Races in Orbit Oct 04 '11 at 09:24
  • 3
    @TomalakGeret'kal: the moderators may decide to merge the two questions though. It may be necessary to vie for their attention. – Matthieu M. Oct 04 '11 at 09:35
  • @Nawaz: if you can improve a question (for example by adding a detailed conceptual level) then you're free to edit it. – Steve Jessop Oct 04 '11 at 11:38
  • 1
    @SteveJessop: You're implying that I knew that possible duplicate. I say, I didn't know that. – Nawaz Oct 04 '11 at 11:58
  • @Nawaz: Sorry, I meant that since you know now, you could do that edit, instead of arguing for the other question to be closed. I can't remember exactly what happens when questions are merged -- you might be wise to take a copy of your question *now*, because if a mod does merge it may be that your question text will vanish, losing any improvements that could be applied to the other question. – Steve Jessop Oct 04 '11 at 12:06
  • @Steve: ...which is a bit of an unfortunate site flaw, in my view. – Lightness Races in Orbit Oct 04 '11 at 16:03
  • If boost is not an option, take a look at my [answer](http://stackoverflow.com/a/28811646/213871) that resembles .NET nullables. – ceztko Mar 02 '15 at 16:37

6 Answers6

40

You could look into Boost.Optional:

struct Person
{
  std::string               Name;
  DateTime                  Birth;
  boost::optional<DateTime> Death;
  //...
};
  • Your Death is "uninitialised" at first.
  • You can then assign a value to it with =, like Death = myDateTime.
  • When Death.is_initialized(), you can use Death.get().
  • Uninitialise it again with Death.reset().

For simple cases like this, though, it's usually considered more coherent to just pick your own blatant sentinel value like, say, a DateTime of "0000-00-00 00:00:00".

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
  • Please expand on this, as to how should it be used? What would be value of `Boost.Optional` for dead persons? – Nawaz Oct 04 '11 at 09:13
  • How did you edit this question and not make the "edited" link show up? – RedX Oct 04 '11 at 09:17
  • 2
    @RedX: I didn't edit the answer! It was always great! ;) – Lightness Races in Orbit Oct 04 '11 at 09:18
  • 1
    No way, first time i entered this topic your answer just said: "boost.optional". – RedX Oct 04 '11 at 09:19
  • @RedX: You must be losing your mind! – Lightness Races in Orbit Oct 04 '11 at 09:21
  • 5
    (http://meta.stackexchange.com/questions/9090/is-there-an-editing-grace-period-on-answers-after-they-have-been-posted) – Lightness Races in Orbit Oct 04 '11 at 09:22
  • I believe you should be able to test `Death` as it is (i.e. no need for `is_initialized()` - AFAIK, it supports safe bool), and `operator*` to get reference (this is purely syntactic sugar though...) – Nim Oct 04 '11 at 09:22
  • @Nim: [safe bool is equiv to `.get() != 0` here](http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/boost_optional/detailed_semantics.html) but `.get()` returns the encapsulated object (or various refs thereof). So it's actually a wrapper around safe-booling the encapsulated object. – Lightness Races in Orbit Oct 04 '11 at 09:25
  • 5
    `reset` and `is_initialized` are deprecated. You can uninitialize by assigning `boost::none` to it, and test for initialization with conversion to bool. – R. Martinho Fernandes Oct 04 '11 at 10:32
  • @RMartinho: Is the documentation wrong then? Should it say that conversion to bool is in fact `.get_ptr() != 0`? – Lightness Races in Orbit Oct 04 '11 at 11:00
  • @ildjarn: conversion-to-bool is deprecated? News to me. – Lightness Races in Orbit Oct 04 '11 at 17:04
  • No, `reset` and `is_initialized` are deprecated, and conversion to bool is recommended, just as @R.MartinhoFernandes said. – ildjarn Oct 04 '11 at 17:20
  • @ildjarn: Please read my comment, and the documentation, again. Conversion to bool is defined by the documentation to be equivalent to `.get() != 0`, not `.get_ptr() != 0`. And the former asserts when the object is not initialised. If the documentation is correct then quite clearly we are not supposed to determine whether our object is initialised via conversion to bool, unless immediate termination of our program is acceptable handling for an answer of "no". – Lightness Races in Orbit Oct 04 '11 at 17:25
  • 2
    @TomalakGeret'kal `reset` and `is_initialized` are documented as deprecated. See for yourself: http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/boost_optional/synopsis.html. I provided the alternatives: `x = boost::none` instead of `x.reset()` and `if(x)` instead of `if(x.is_initialized())`. – R. Martinho Fernandes Oct 04 '11 at 17:27
  • @RMartinho: Why is everybody obsessing over those two functions? I'm talking about the conversion to bool. Read my comments again please. You _cannot_ "test for initialization with conversion to bool" unless either: (a) the documentation is wrong about what conversion to bool does; (b) you don't mind `BOOST_ASSERT`ions as flow control. – Lightness Races in Orbit Oct 04 '11 at 17:28
  • 1
    @Tomalak : Everyone is "obsessing" over those two functions because your answer says to use them and they're deprecated. Regarding the safe-bool operator, the documentation is **obviously** wrong, as `get() != 0` is **obviously** nonsensical. A cursory glance at the source code also reveals the safe-bool operator to be safe to use when the object is uninitialized. – ildjarn Oct 04 '11 at 17:41
  • If boost is not an option, take a look at my [answer](http://stackoverflow.com/a/28811646/213871) that resembles .NET `Nullable`. – ceztko Mar 02 '15 at 16:38
6

Depends on DateTime - like @Tomalak says in his answer, boost::optional<> is a generic solution. However if for example your DateTime is a boost::posix_time::ptime, then there is already support for special values (for example not_a_date_time or pos_infin) - you could use these.

Nim
  • 32,149
  • 2
  • 56
  • 98
5

Every project I've worked on has had some sort of Fallible, Maybe or Nullable template class. (The actual name tends to reflect what the application first needed it for: Fallible as a return value, Nullable to model databases, etc.). More recently, Boost has introduced boost::optional; regretfully, they use implicit conversions instead of an isValid (named) function, which results in noticeably less readable code (to the point where I'd avoid it, except maybe to implement my own Maybe).

James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • 1
    If you don't like the boolean conversion, why not just add a free function template: `template bool isValid(const boost::optional &o) { return o; }`? Seems easier than wrapping the whole class. – Steve Jessop Oct 04 '11 at 11:35
  • 2
    @SteveJessop Well, you also want to get rid of the pointer operations; it's **not** a smart pointer, so it shouldn't look like one. In many ways, `boost::optional` seems to have suffered from feature creep. (Of course, the same can be said of my `Fallible`---or probably any other class that's been around a long time and has been used in a variety of applications.) – James Kanze Oct 04 '11 at 12:39
1

You can do what legions of programmers did before you! Use a specific value as "not present"... For example I've heard that "1 Jan 2000" was pretty common :-) :-) For interoperability reasons you could use "19 Jan 2038 03:14:07 UTC" :-) :-) (it's a joke, if it isn't clear. I'm referencing the Y2K problem and the Y2038 problem. I'm showing the problem of using "special" dates as states... Things like 11-11-11 and similar)

The maximum/minimum value of your DataTime is probably more correct :-) And it's still wrong, because you are mixing a "state" with a "value". Better that you rebuild the Nullable type in C++ (in the end it's quite easy: a templated class with a bool for null/not null and the T field)

xanatos
  • 102,557
  • 10
  • 176
  • 249
1

as death is particularly unlikely to be any time before birth you could just as well set it to birth - 1 initially and have it changed on the actual event. in more common terms you'd probably call birth - 1 a sentinel value or placeholder value. you could also pick a constant value low enough not to be mistaken for a real value, but that assumes you have some knowledge of your data.

mtijn
  • 3,440
  • 2
  • 26
  • 54
-2

I would create a static member which represent null value, than compare the address of date of death to the address of the static object. If they are equal the value is NULL.

WebMonster
  • 2,833
  • 2
  • 19
  • 28