wal*_*nut 9 c++ exception language-lawyer
考虑以下程序:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
Run Code Online (Sandbox Code Playgroud)
带有 libstdc++ 的 GCC 和 Clang 调用std::terminate并使用消息中止程序
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Run Code Online (Sandbox Code Playgroud)
使用 libc++ 段错误构建异常的 Clang。
见神箭。
编译器的行为是否符合标准?标准[diagnostics.range.error] (C++17 N4659)的相关部分确实说std::range_error有一个const char*构造函数重载,应该优先于const std::string&重载。该部分也没有说明构造函数的任何先决条件,而只说明了后置条件
后置条件:
strcmp(what(), what_arg) == 0。
如果what_arg是空指针,则此后置条件总是具有未定义的行为,那么这是否意味着我的程序也具有未定义的行为并且两个编译器的行为都一致?如果不是,应该如何阅读标准中这种不可能的后置条件?
再想一想,我认为这对我的程序来说一定意味着未定义的行为,因为如果不是,那么也将允许(有效)指针不指向以空字符结尾的字符串,这显然没有意义。
因此,假设这是真的,我想将问题更多地集中在标准如何暗示这种未定义的行为上。是否因为调用也具有未定义的行为或先决条件被简单地遗忘了后置条件的不可能性?
受到这个问题的启发。
Sur*_*urt -2
这又回到了一个基本问题:从 nullptr 创建 std::string 是否可以?它应该做什么呢?
如果 s 是空指针,如果 n == npos,或者 [first,last) 指定的范围无效,则会导致未定义的行为。
所以当
throw std::range_error(nullptr);
Run Code Online (Sandbox Code Playgroud)
被称为实现试图做出类似的事情
store = std::make_shared<std::string>(nullptr);
Run Code Online (Sandbox Code Playgroud)
这是未定义的。我认为这是一个错误(没有阅读标准中的实际措辞)。相反,自由开发者可以制作类似的东西
if (what_arg)
store = std::make_shared<std::string>(nullptr);
Run Code Online (Sandbox Code Playgroud)
但是捕手必须检查 nullptr,what();否则就会崩溃。因此,std::range_error应该像其他一些语言一样分配一个空字符串或“(nullptr)”。