Constructors and evil initializers in C++
jmmv.dev
jmmv.dev
The heap-allocated version is so much worse! Not only do you have to pay for allocating and freeing it, every time you reference it, you're gonna have to chase a pointer. Imagine if you had 1000 of these in a vector: the stack allocated ones would be right there next to each other, incredibly cache friendly. The heap-allocated ones are gonna cache miss constantly, and there's ZERO chance the loop is going to auto-vectorize. Show this to a data-oriented design person and watch their eyes start bleeding.
And for what? So you can return null? Just return a std::optional from the factory method if you care so much (or boost::optional if you're on pre-C++17). Or use exceptions. Or just accept the fact that it might return the default value. All of those options are better than the final one. If you're choosing to program in C++, it means you should care about performance. If you don't care about performance for this kind of stuff, just use a higher-level language.
To avoid crashing, the caller has to validate the parameters before calling the constructor, and handle the invalid parameters at this time rather than in the exception handler, but in the end it's the same.
Anyways, for small classes like this one literally anything is better than implicitly forcing the user to allocate on the heap. The author should have pointed out the massive drawbacks of this approach.
That's lazy. Why not return via error?
- An old library we were using for a nice-to-have feature started crashing our server when an ACL change got rolled out. We had to disable and then permanently remove the feature.
- When reusing a server-side component in a client-side service, the implications of crashing are different. When your servers crash at Google it generally gets detected and the binary/config gets automatically rolled back and things restard cleanly. On a systemd service? Not so straightforward. Now you have to rework the component to do proper error handling, or start thinking very carefully about how your package interacts with the rest of the system.
So yeah I guess let-it-crash has its place but that place is not 'everywhere'.
So if you do it on a C++ project, you will get strange looks from the team, so one just shrugs and codes the same way as always.
[0] - https://www.youtube.com/watch?v=IaHirnQrL14 [1] - https://dannypsnl.github.io/blog/2017/12/23/cs/type-driven-d...
Maybe the situation has gotten better since constexpr was introduced, though.
Not to mention, this still only works for very simple constraints; if you want to validate complex properties, doing it with types becomes almost a research problem (even something like 'this array must be sorted' encoded fully in types is going to add a lot to your program's length).
Finally, the whole rich type culture is, like you point out, missing in C++. So, even if the whole team is on board with it, you will have to work with libraries that don't prove such complex properties about the types of values they produce, putting the entire burden on your own program - either roll your own libraries or as lots of complex-type-annotated wrappers.
Since C++11? Yes.
> even something like 'this array must be sorted' encoded fully in types is going to add a lot to your program's length
template< class Cmp, class Array> class Sorted;// The type of your parameter.
I think this could be implemented even in C++03. template< class Cmp, class Array> class Sorted;// The type of your parameter.
That type achieves nothing. std::sort doesn't return an instance of Sorted, and nothing prevents me from populating an instance of Sorted with an array that is not, in fact, sorted. So, in my opinion the following two pieces of code are perfectly equivalent: void binary_search(const vector<int> sorted_arr);
void binary_search(const Sorted<Cmp,vector<int>> arr);
Considering these as functions in a program that otherwise uses the mainstream C++ library ecosystem, they are equally likely to be called with an unsorted array.In Idris or probably even in C++ with significant effort, you could create a type that can't represent an unsorted array, but that is, again, an almost research-level endeavour (and adding more complex constraints it will quickly evolve).
The idea of taking params that can't be invalid is that validity is enforced by class invariant.
> std::sort doesn't return an instance of Sorted
It can't, because it sorts in place and therefore has no control over the sequence. Sorted constructor would sort to guarantee success.
Inviting the question: why not move that validation to a manager class that validates the inputs and returns the desired instance, or a suitable error indication if naughty?
It should be possible to template a class that only accepts positive integers.
All your type traits/SFAINE/concepts/static_asserts happen at compile time, you can’t use them to do any checks on the value of a type, unless it’s a value available at compile time.