Подтвердить что ты не робот

Лучший способ представить Nullable-член в С++?

Возможный дубликат:
Невозможные значения в С++

Каков наилучший способ представления элемента с возможностью слияния в С++?

В С# мы можем использовать тип Nullable<T>. Такой тип данных очень необходим, поскольку не все может иметь значимую ценность. Этот важный тип данных, который @Jon Skeet провел целую главу, охватывал более 27 страниц, описывая только Nullable<T> в своей выдающейся книге С# в глубине.

Одним простым примером может быть класс Person 1 определяемый как:

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

Как человек всегда имеет дату рождения, поэтому член Birth из вышеперечисленного класса всегда будет иметь какое-то значимое значение. Но как насчет Death? Что должно стоить, если человек жив? В С# этот член может быть объявлен как Nullable<DataTime> 2 который может быть назначен null, если человек жив.

В С++, что является лучшим способом решить эту проблему? На данный момент у меня есть только одно решение: объявите элемент как указатель:

 DataTime *Death;

Теперь его значение может быть nullptr, когда человек жив. Но это заставляет использовать new для мертвого человека, поскольку он будет иметь некоторое действительное значение. Это, в свою очередь, подразумевает, что нельзя полагаться на кодо-семантический код по умолчанию, сгенерированный компилятором. Программист должен написать copy-constructor, copy-assign, destructor после правила из трех (С++ 03), или в С++ 11, правило из пяти.

Итак, у нас есть лучшее, элегантное решение этой проблемы, чем просто его указатель?


1. Другие примеры включают таблицы реляционной базы данных, так как во многих СУБД столбцы могут быть обнуляемы.

2. Для этого также есть сокращение. Можно написать DataTime?, который точно такой же, как Nullable<DateTime>.

4b9b3361

Ответ 1

Вы можете посмотреть Boost.Optional:

struct Person
{
  std::string               Name;
  DateTime                  Birth;
  boost::optional<DateTime> Death;
  //...
};
  • Ваш Death сначала не инициализирован.
  • Затем вы можете присвоить ему значение =, например Death = myDateTime.
  • Когда Death.is_initialized(), вы можете использовать Death.get().
  • Uninitialise снова с Death.reset().

Для простых случаев, подобных этому, обычно считается более последовательным, чтобы просто выбрать ваше собственное вопиющее значение дозорного, как, например, DateTime из "0000-00-00 00:00:00".

Ответ 2

Зависит от DateTime - как говорит @Tomalak в своем ответе, boost::optional<> является общим решением. Однако, если, например, ваш DateTime является boost::posix_time::ptime, тогда уже есть поддержка специальных значений (например, not_a_date_time или pos_infin) - вы можете использовать их.

Ответ 3

Каждый проект, над которым я работал, имел Fallible, Maybe или Nullable класс шаблона. (Фактическое имя имеет тенденцию отражать то, что приложение сначала нуждалось в этом для: Fallible в качестве возвращаемого значения, Nullable для моделирования баз данных и т.д.). Совсем недавно Boost введено boost::optional; к сожалению, они используют неявные преобразования вместо функции isValid (named), что приводит к заметному менее читаемый код (до такой степени, что я бы избегал этого, за исключением, может быть, реализовать мой собственный Maybe).

Ответ 4

Вы можете делать то, что сделали легионы программистов перед вами! Используйте определенное значение как "нет"... Например, я слышал, что "1 янв 2000" был довольно распространенным:-):-) По соображениям совместимости вы можете использовать "19 января 2038 03:14:07 UTC",:-):-) (это шутка, если это не ясно. Я ссылаюсь на проблему Y2K и проблему Y2038.Я показываю проблему использования "специальных" дат как состояний... Такие вещи, как 11 -11-11 и т.п.)

Максимальное/минимальное значение вашего DataTime, вероятно, более корректно:-) И все равно неправильно, потому что вы смешиваете "состояние" со значением "значение". Лучше, чтобы вы перестроили тип Nullable на С++ (в конце концов это довольно просто: шаблонный класс с bool для null/not null и поле T)

Ответ 5

поскольку смерть особенно маловероятна, чтобы быть в любое время до рождения, вы могли бы точно установить ее на рождение - 1 изначально и изменить ее на фактическое событие. в более распространенных терминах вы, вероятно, называете рождение - 1 значением дозорного или заполнительным значением. вы также можете выбрать постоянное значение, достаточно низкое, чтобы его не принимали за реальную ценность, но это предполагает, что у вас есть некоторые сведения о ваших данных.

Ответ 6

Я бы создал статический член, представляющий нулевое значение, чем сравнение адреса даты смерти с адресом статического объекта. Если они равны, значение равно NULL.