Im considering if something like the following be a good work-around ?
Const members are usually problematic. They prevent assignability, and they prevent move construction. That said, assignability might not always be needed for an exception type, and the class in question is fast and noexcept to copy anyway.
So the drawbacks may not be so significant in this case, but neither are the advantages. Your class already has pre-condition in the constructor for the null termination, and the access is encapsulated to member functions (and friends) of A
.
More fundamentally however, your exception class is highly prone to misuse. Consider following typical scenarios:
throw A(some_local_string.c_str());
char local_buffer[N];
// fill local_buffer with formatted message
throw A(local_buffer);
These result in undefined behaviour.
Why is that more prone to misuse than eg. std::string_view
It is in fact the use of std::string_view
that makes your exception class prone to misuse. Storing a const char*
would have the same problem.
std::string_view
(and const char*
) is fine for function arguments for example, when the function doesn't store the view for longer than the caller of the function exists. But exceptions are pretty much solely used so that they live longer than their callers because they are pretty much always thrown, which unwinds the caller.
if I/we had c_string_view from the get go - we could just use that in the constructor instead of const char* to show intent as well.
Accepting c_string_view
doesn't really solve the problem since it doesn't by itself imply that it will be stored any more than const char*
would. Best you can do is to document how the class can be used, which is somewhat unsatisfactory when those uses exclude common patterns of exception usage.
Maybe if you name the class static_string_exception
or similar to make the limitations more obvious.
from commenters:
I'd just take an store a std::string ...
.. you should rather use good 'ol std::string
std::string
is not an ideal solution either. It would prevent the copy constructor / assignment from being non throwing (not strictly, but other alternative is program termination), which would not be good for an exception type. For example, standard exception types are not allowed to have throwing copy constructor / assignment.
Once you reach for the copying route, you might as well use std::runtime_error
which does that for you.