Rather than have a throwing constructor, you can have a function that returns either an object, or nothing. Callers will have to handle the possibility of there being no object.
This isn't quite "Have my object", but it does satisfy "always be sure that size is positive when I use it".
From your example:
class MyObj {
private:
MyObj(double size) {m_size = size};
double m_size;
public:
double GetSize() {return m_size};
static std::optional<MyObj> Create(double size) {
if(size<0) return std::nullopt;
return MyObj(size);
}
}
The reasoning you were supplied with is somewhat faulty. The immediate caller of a function does not have to be wrapped in try ... catch .... It can be some distance up the call stack, where an appropriate action can be taken. The try ... catch ... you describe in point 2 is such a place, and is sometimes called the "last-chance exception handler".
It is also somewhat worrying that part of the last-chance exception handler is an autosave, because by definition, you don't know what's gone wrong when you get there, so you might be saving an inconsistent state.
Point 3 is also faulty reasoning. You've swapped the possibility of having a "Something went horribly wrong" message in production, for the possibility of something going horribly wrong silently, and corrupting all your data. It doesn't actually address "We cannot be sure that future programmers will correctly use your object and handle correctly all edge cases", it just makes errors quieter.