【发布时间】:2020-07-10 10:51:52
【问题描述】:
考虑以下程序:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
带有 libstdc++ 的 GCC 和 Clang 调用 std::terminate 并使用消息中止程序
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
在构造异常时使用 libc++ 段错误。
见godbolt。
编译器的行为是否符合标准?标准[diagnostics.range.error] (C++17 N4659) 的相关部分确实说std::range_error 有一个const char* 构造函数重载,应该优先于const std::string& 重载。该部分也没有说明构造函数的任何前置条件,只说明后置条件
后置条件:
strcmp(what(), what_arg) == 0.
如果what_arg 是空指针,则此后置条件始终具有未定义的行为,这是否意味着我的程序也具有未定义的行为并且两个编译器的行为一致?如果不是,应该如何阅读标准中这些不可能的后置条件?
再想一想,我认为这对我的程序来说一定意味着未定义的行为,因为如果不是这样,那么(有效的)不指向空终止字符串的指针也将被允许,这显然没有意义。
因此,假设这是真的,我想将问题更多地集中在标准如何暗示这种未定义的行为上。是因为后置条件的不可能性,调用也有未定义的行为,还是前置条件被遗忘了?
灵感来自this question。
【问题讨论】:
-
听起来std::range_error 被允许通过引用存储东西,所以我不会感到惊讶。在传递
nullptr时调用what()可能会导致问题。 -
@Chipster 我不确定你到底是什么意思,但在再次考虑这个问题后,我认为这一定是未定义的行为。我已经编辑了我的问题,以便更多地关注如何标准措辞暗示未定义的行为。
-
如果
nullptr被传递,我认为what()必须在某个时候取消引用它才能获得值。那将取消对nullptr的引用,这充其量是有问题的,并且肯定会崩溃是最坏的。 -
不过我同意。它必须是未定义的行为。但是,将其纳入解释原因的充分答案超出了我的技能范围。
-
我认为这是一个前提条件,即参数指向一个有效的C字符串,因为
strcmp用于描述what_arg的值。无论如何,这就是相关的section from the C standard 所说的,由<cstring>的规范引用。当然,措辞可以更清楚。
标签: c++ exception language-lawyer