Standard library defect #254 which covers the addition of new exception constructors:
std::logic_error::logic_error(const char* what_arg); std::runtime_error::runtime_error(const char* what_arg); // etc.
gives as rationale the idea that storing std::string
s opens up some cans of worms relating to potentially problematic memory allocation.
However, following initiation of a discussion by orlp in the Lounge, it strikes me that unless the standard were to mandate that what_arg
were only ever a string literal (or a pointer to some other buffer of static storage duration), it would have to perform a copy of the C-string anyway in order to maintain the well-definedness of member function what()
.
That's because:
void bar() { char buf[] = "lol"; throw std::runtime_error(buf); } void foo() { try { bar(); } catch (std::exception& e) { std::cout << e.what() << '\n'; // e.what() points to destroyed data! } }
But I cannot see any such mandate. In fact, whether exception objects deep copy what_arg
or not appears to be entirely unspecified.
If they do, then a large chunk of the rationale for adding the overloads in the first place (eliminating additional allocations) appears to be entirely vacuous.
Is this potentially a standard defect, or am I missing something here?
Is this just a case of "programmer: don't pass dangling pointers anywhere"?
This allows (or at least is apparently intended to facilitate--see more below) the implementation to eliminate copies in cases where it can detect (by means that are not themselves standardized) that what's being passed is a string literal or something else with static storage duration.
Just for example, let's assume a compiler pools all string literals together into a range delimited by __string_literals_begin
and __string_literals_end
. Then somewhere inside the constructor for std::exception
it could have code on the general order of:
namespace std { exception::exception(char const *s) { if (in_range(s, __string_literals_begin, __string_literals_end)) { stored_what = s; destroy_stored_what = false; } else { stored_what = dupe(s); destroy_stored_what = true; } // ... } exception::~exception() { if (destroy_stored_what) delete_string(stored_what); }
The final comment in the linked DR states:
[ Oxford: The proposed resolution simply addresses the issue of constructing the exception objects with const char* and string literals without the need to explicit include or construct a std::string. ]
So based on the comments at the time, the committee was aware that these overloads didn't fill all needs, but did address (what was at least perceived as) a need.
It's (almost) certainly true that an implementation could provide these overloads even without their being mandated by the standard--nonetheless, the committee seems to have been convinced that adding them was useful, primarily (if not exclusively) for the case outlined above--doing only a shallow copy when a string literal is passed to the ctor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With