Constructors Shouldn't Throw
Posted by jcbhmr 3 days ago
Comments
Comment by MiroslavPokorny 3 days ago
Comment by quietbritishjim 3 days ago
If you really wanted to go down the author's route, then you'd at least make the constructor private, and have a static method that returns a valid constructed object or an error, so you never have e.g. an "unconnected" database connection:
class DatabaseConnection {
public:
static std::expected<DatabaseConnection, ConnectionError> connect(/* ... */);
// ...
};
But I wouldn't even bother with this - it's exceptions with extra steps. I seem to remember that Stroustrop said in the Design and Evolution of C++ that the main reason for adding exceptions in the first place was to allow a failure result from a constructor.Comment by quuxplusone 3 days ago
No, STL containers such as std::vector definitely do not assume (nor require) non-throwing constructors. They just do the right thing. Yes, cleanup is messier than it would otherwise be.
STL containers do require that your destructors not throw (but do not require that they be noexcept).
Comment by quietbritishjim 3 days ago
Comment by quuxplusone 2 days ago
https://stackoverflow.com/questions/66459162/where-do-standa...
Background: https://quuxplusone.github.io/blog/2022/08/26/vector-pessimi...
Comment by quietbritishjim 23 hours ago
Comment by account42 2 days ago
Comment by xg15 2 days ago
I find it interesting that programming newbies are usually scared of C/C++ because "you have to deal with pointers" unlike in Java, Python, JavaScript etc.
It's interesting because all those languages of course use pointers and heap-allocated objects. The thing that sets the other languages apart is that they only allow pointer access and thus can make the language easier to reason about.
So what's "unusual" in C, C++ and Rust is really the parts that are not pointers, i.e. the ability to manage a data structure and its layout in memory directly, with all its consequences.
Comment by msm_ 2 days ago
Comment by x-complexity 3 days ago
The given X should be subjected to the requirements necessary to create a valid Y. If the requirements fail, you have two main choices: Create an invalid Y, or reject X & return an error in however you want it to be returned.
The former leaks said rejections throughout the rest of the type's/object's code, & makes more work for me to parse through where & how the problem occurs.
The latter option creates a cleaner system altogether, due to rejecting sections of invalid states. Not all will be rejected, but the fact that some do get rejected already makes its workload comparatively lighter than the former.
I know which my lazy bum would pick.
Comment by nitwit005 1 day ago
That's just a constructor you've given another name.
Comment by AnimalMuppet 3 days ago
No, I expect it to perform the functionality of bringing the object into a valid state - whatever functionality that takes. And if it can't, then throwing is the next option, because it's preferable to having an object in an invalid state.
Comment by robotresearcher 2 days ago
I prefer to have constructors that can’t go wrong (except trivially for OOM that ends the whole process), and then manage their dynamic state after construction.
Comment by AnimalMuppet 2 days ago
Look, for your problem, you probably want to do what the fine article said - to have "invalid connection" be a valid state of the object.
Comment by robotresearcher 2 days ago
Comment by AnimalMuppet 2 days ago
Comment by Panzerschrek 3 days ago
It's a bad advice. It's preferred instead to have a connect function returning a database instance in a successful case or an error otherwise.
Comment by account42 2 days ago
Comment by oraro 3 days ago
Comment by robotresearcher 2 days ago
For example, if arg values can’t be negative, use an unsigned arg type.
Comment by verdverm 3 days ago
Comment by quietbritishjim 3 days ago
At one point they needed a custom build of AIX because its executable size limit of 1GB wasn't enough for the big. This was about 20 years ago, I suppose that seems quaint now.
Comment by verdverm 3 days ago
for others context, bigs were the compiled binary deployed to a server, a composition of BB terminal functions, bin packing style
Comment by DemocracyFTW2 3 days ago
wrong
Comment by verdverm 3 days ago
If we interpret "constructor" more generically as setup, one of my preferred languages, Go, does not have constructors, but we do have similar setup functions, which typically return errors, and we handle them as soon as possible without breaking out from the designed control flow.
Comment by DemocracyFTW2 3 days ago
Comment by stevekemp 3 days ago
I guess you're thinking more of package constructor methods, such as NewFoo, which tend to return the new object and an error value.
Comment by verdverm 3 days ago