В последнее время я вижу много материалов об универсальном программировании, и я до сих пор не могу уложиться в одну вещь, когда проектирую типы. Я не уверен, что это лучший способ, позвольте мне объяснить.
Для некоторых типов естественно предоставить конструктор по умолчанию. Все возможные конструкции этого типа будут действительными, или значение по умолчанию имеет смысл, поэтому имеет смысл указать значение по умолчанию. Это относится к базовым типам.
Позже есть некоторые типы, для которых их построение по умолчанию не дает значения. Например, в стандартной библиотеке у нас есть std::function<Sig>
и std::thread
, например. Тем не менее, они конструируются по умолчанию, даже если они не содержат значения.
Позже у нас есть предложенный optional<T>
в стандарте. Имеет смысл использовать его для базовых типов, так как для базовых типов все возможные присваивания представляют допустимое значение (кроме double и float NaN), но я не понимаю, как бы вы использовали его для thread
или std::function<Sig>
, так как эти типы не содержат «значение» при создании. Это КАК ЕСЛИ бы эти типы имели «необязательные», встроенные в тип напрямую.
Это имеет эти недостатки. Поскольку нет «естественной» конструкции по умолчанию (или значения), например, с int:
- Теперь я должен засорить свой класс
if (valid)
во всем своем дизайне и сигнализировать об ошибке. ИЛИ - сделать его менее безопасным, если я не сделаю эту проверку. Предварительное условие -> назначить перед использованием, если построено по умолчанию.
Поэтому, когда я хочу разработать тип, я всегда задаюсь вопросом: должен ли я сделать его конструируемым по умолчанию?
Плюсы:
- Легче повторно использовать в более общих контекстах, потому что мой тип будет легче моделировать SemiRegular или Regular, если я добавлю соответствующие операции.
Минусы:
- Засорите весь класс операторами
if
или заключите контракт с пользователем, в котором использование класса более небезопасно.
Например, допустим, у меня есть класс Song
с идентификатором, исполнителем, названием, продолжительностью и годом. Для стандартной библиотеки очень удобно делать тип по умолчанию конструктивным. Но:
- Я не могу просто найти естественный способ создать «Песню по умолчанию».
- Я должен засорить
if (validsong)
или сделать его небезопасным для использования.
Итак, мои вопросы:
Как мне разработать тип, который не имеет «естественных (как по значению)» значений по умолчанию? Должен ли я предоставить конструктор по умолчанию или нет?
В случае, если я предоставлю конструктор по умолчанию, как
optional<T>
впишется во всю эту головоломку? Я считаю, что создание типа, который не является «естественно» конструктивным по умолчанию, предоставляет конструктор по умолчанию, что делаетoptional<T>
бесполезным в этом случае.Следует ли использовать
optional<T>
только для типов, область значений которых заполнена, то есть я не могу присвоить недопустимое значение его представлению, потому что все они содержат значение, например, int?Почему такие типы, как
std::function<Sig>
, изначально стали конструируемыми по умолчанию в стандарте? При построении он не содержит значения, поэтому я не понимаю, почему должен быть предоставлен конструктор по умолчанию. Вы всегда можете сделать:optional<function<void ()>>
, например. Является ли это просто выбором дизайна, и оба варианта допустимы, или в данном случае есть один дизайн, заключающийся в выборе конструктивного элемента по умолчанию и не по умолчанию, который превосходит другой?