Just because you can stick your hand in a blender, that doesn’t mean you should. Similarly, you can throw
anything, but that doesn’t mean you should throw something that’s not derived at some level from
std::exception.
If you can’t find an existing exception type that suitably conveys what you need to convey, then you should extend std::exception to
create one.
Specifically, part of the point of throwing exceptions is to communicate about the conditions of the error, but primitives have far less ability to
communicate meaningfully than exceptions. And, the creation of some other object type could itself throw an exception,
resulting in program termination.
Further, catching non-exception types is painful and fraught with the potential for (further) error.
Noncompliant code example
throw 42; // Noncompliant
throw "Invalid negative index."; // Noncompliant
throw std::string("Permission denied"); // Noncompliant
throw nullptr; // Noncompliant
Compliant solution
throw std::domain_error("User ID not found.");
throw std::out_of_range("Invalid negative index.");
throw std::system_error(EACCES, std::system_category());
throw std::invalid_argument("Unexpected null 'user_id' argument.");