To be fair, there's a reason for the pattern with init methods you're describing.
Without prejudice on any other reasons, the most common reason for this pattern I've seen is people thinking in languages that basically don't have constructors, yet writing C++. It's not a good reason.
The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive.
C++ uses deferred destruction as a standard tool to solve a variety of problems.
> which may be read after being moved-from (a valid behaviour in some systems code contexts)
std::move as applied to standard library types will leave the object in a "valid but unspecified state".[1] If you're leaving the object in an invalid state (one where the invariants are broken), you're not writing idiomatic C++.
This makes sense for objects that can enter an equivalent invalid state after successful construction as the result of a method call (e.g. a file or stream).
For objects that don't have that property, you're just exchanging one kind of badness in the design for a different but ultimately equivalent badness.
This was one of the best decisions that Rust and Go did; not have constructors. In C# this is super annoying too, specially when you need an async operation to construct a type. This is usually done by having an private constructor and then using a static public method to create the type.
Rust and Go have no form of a conversion operator (even if not a constructor), which makes scripting a type system essentially impossible. Numeric libraries in both of those languages are extremely cumbersome, largely for this reason.
That's a bold statement, considering that many of the largest C++ code bases - including at least one of the few remaining C++ compilers! - don't use exceptions.