joboccara / NamedType

Implementation of strong types in C++
MIT License
764 stars 85 forks source link

Add Ability to Disable Default-Constructor #68

Open stellarpower opened 2 years ago

stellarpower commented 2 years ago

Somewhat contrary to #14, for my situation, I'd potentially like to disable default-construction, ideally by including a skill.

I think default-construction makes a lot of sense in many situations. And also, non default-constructible types can cause issue with some classes - a particular container may require this, for example.

But to think in a wider context, where we use our named types is to ensure that we don't have nonsensical values entering our code. We generally want to restrict places where implicit conversions are happening, and be explicit instead. Default-construction would be one place this is implicit.

I am using some NamedTypes to wrap things like ID numbers between opaque structures, frame numbers in a video, various types that effectively work like foreign keys. A default constructor for an underlying type makes some sense - an empty string would be a good starting point, and if I'm going to have a numerical value, starting it at zero also makes sense. But to have a default-constructed ID doesn't make much sense in my applicaion - that ID has to come from somewhere, being correlated with a separate entity. A frame number has an implicit meaning in respect to the timestamps in a video frame. Just starting it at zero when we have a new one sort-of violates the principles of strong typing in the first place. Thus, a default-constructor, even if only temporarily, effectively forms a relationship that doesn't really exist, by forming a correlation between the element with ID 0, and the current scope where we created it.

The place I think I've seen this in my code is using std::map::operator[]. Unlike calling at(), operator[] will insert a default-constructed value when the given key doesn't exist. This makes a lot of sense in some situations, but I think for others, it may be a disadvantage. Whether or not this turns out to be a bug in my application, if we have a map betwee two different IDs/keys, inserting a default value when the (map's) key isn't found then violates the point of the mapping in the first place. This is an issue in my code, I should probably correct it to call at() and catch an exception instead, however, in this case, disabling the default-constructor should prevent the code from compiling inside map.

I'd therefore like to suggest that DefaultConstructible be made an inheritable skill, in the same manner as other skills, and perhaps one that could be turned off, by adding somehting like a NotDefaultConstructible pseudo-skill that would allow disabling this skill - otherwise, it may be that skills like Arithmetic would then need to be broken apart and specified as a long list, just to disable the DefaultConstructible.

Thanks :)

r-barnes commented 2 years ago

I would also find this helpful!

(Also: thanks for putting this together - NamedType is pretty cool!)