Consider the following program:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
GCC and Clang with libstdc++ call std::terminate and abort the program with the message
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Clang with libc++ segfaults on construction of the exception.
See godbolt.
Are the compilers behaving standard-conform? The relevant section of the standard [diagnostics.range.error] (C++17 N4659) does say that std::range_error has a const char* constructor overload which should be preferred over the const std::string& overload. The section also does not state any preconditions on the constructor and only states the postcondition
Postconditions:
strcmp(what(), what_arg) == 0.
This postcondition always has undefined behavior if what_arg is a null pointer, so does this mean that my program also has undefined behavior and that both compilers act conformant? If not, how should one read such impossible postconditions in the standard?
On second thought, I think it must mean undefined behavior for my program, because if it didn't then (valid) pointers not pointing to null-terminated strings would also be allowed, which clearly makes no sense.
So, assuming that is true, I would like to focus the question more on how the standard implies this undefined behavior. Does it follow from the impossibility of the postcondition that the call also has undefined behavior or was the precondition simply forgotten?
Inspired by this question.