Yes, throwing an exception is the standard way of handling a constructor that fails.
Well, constructor's don't have any kind of return value, so, many people use an Init() type function that returns an error code of some sort.
The standard way would be to throw an exception from the constructor. (I dont do that either.)
I would never do anything "complicated" inside the constructor, like loading images, setting up textures, etc. I would just initialise member variables to a known state and leave the other stuff for an init function/method or using getters/setters, depending on the situation. I wouldn't do anything that could throw exceptions inside a constructor. Throwing an exception inside a constructor sounds like a bad thing to me. Is this really the standard way?
Yes, but no, maybe.
In theory it's the standard mechanism (or using an out-parameter or a zombie object state)... however, I've never actually seen exceptions used in the professional games industry.
Over the past 10 years I've used about 7 different professional C++ game engines (on about a dozen different products), and they all avoided the use of C++ exceptions completely.
I don't want to turn this into a "Are exceptions good/bad" thread, as that's a different topic -- but C++'s exceptions should not be compared to C#/Java/Python/etc's exceptions -- they are a completely different beast. Also C++ is a very complex language, to the point where almost every project leader will define an acceptable sub-set of the language to be used.
For large/complex professional game engines, this commonly includes:
- don't use the standard new/delete/malloc/free (use some engine-specific replacement / wrapper),
- don't use std containers (as they will call new, and custom allocator mechanisms are broken),
- don't use exceptions (as writing to satisfy the "strict exception safety" guarantee is hard in C++, there's a performance impact, and some gaming platforms may not even support them),
- don't use RTTI or dynamic_cast,
and sometimes includes (these used to be common 10 years ago, but not so much today).
- don't use anything in std,
- don't use templates.
What about occasions where the error is 'not the end of the world' and the fallback option might not even have anything to do with that class (i.e. you don't need that class if the init() fails).
Can you give an example of where this would happen?
In OO, it should be extremely rare to find a valid case for a constructor to fail.
To go off on a rant for a moment --
OO here doesn't mean that you're using an OOP language and you're using keywords like class... It means that you're making use of the large body of software design and engineering knowledge that's been collected under that moniker.
To use a straw-man example of what's wrong here, let's say that we've got a Texture class, responsible for managing the lifetime of pixel-data inside the GPU, who's constructor loads an image file from disk. This is a problem because errors can occur during file loading, such as FILE_NOT_FOUND. If that occurs, you'd have to abort from inside the constructor!
Bzzt. You just broke the SRP (Texture is responsible for GPU-resource lifetime management AND disk IO logic), so you're actually using your own methodology here, you're not using OO!
While we're reading up on SRP, we also decide to read about DI and IoC.
Now, we end up with a TextureLoader class, who opens a file, handles FILE_NOT_FOUND errors, and then once it's actually able to load the pixel data from disk, only then is a Texture object constructed and passed that data. Wow, after actually using OO, this whole aborting-construction "problem" went away, look at that...
So, I would treat "I need to throw an exception from this constructor" as a code-smell, indicating that you probably need some DI and IoC up in yo code.