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 incl开发者_如何学运维ude 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>
.
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
=
, likeDeath = myDateTime
. - When
Death.is_initialized()
, you can useDeath.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".
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.
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
).
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.
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)
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.
精彩评论