Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 introduced exception constructors taking `const char*`. But why?

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::strings 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"?

like image 681
Lightness Races in Orbit Avatar asked Mar 14 '15 18:03

Lightness Races in Orbit


1 Answers

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.

like image 194
Jerry Coffin Avatar answered Oct 14 '22 03:10

Jerry Coffin