问题描述
请考虑以下程序:
#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
并通过消息中止程序
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
带有libc ++ segfault的Clang构造异常.
Clang with libc++ segfaults on construction of the exception.
请参见 godbolt .
编译器是否符合标准?标准 [diagnostics.range.error] 的相关部分(C ++ 17 N4659)确实说std::range_error
具有const char*
构造函数重载,应优先于const std::string&
重载.本节也没有说明构造函数上的任何前提条件,而只说明了后置条件
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
如果what_arg
是空指针,则此后置条件始终具有未定义的行为,那么这是否意味着我的程序也具有未定义的行为并且两个编译器的行为一致?如果不是,应该如何阅读标准中如此不可能的后置条件?
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.
推荐答案
来自文档:
这说明了为什么会出现段错误,API确实将其视为真实字符串.通常,在cpp中,如果某些东西是可选的,则将有一个重载的构造函数/函数,它不需要任何不需要的内容.因此,将nullptr
传递给未说明可选内容的函数将是未定义的行为.通常,API不会使用C字符串除外的指针.因此,恕我直言,可以安全地假设为期望const char *
的函数传递nullptr将是未定义的行为.对于这些情况,较新的API可能更喜欢std::string_view
.
This shows why you get segfault, the api does really treat it as a real string.In general in cpp if something was optional, there will be an overloaded constructor/function that does not take what it does not need. So passing nullptr
for a function that does not document something to be optional is going to be undefined behavior. Usually API's don't take pointers with the exception for C strings. So IMHO it is safe to assume passing nullptr for a function that expects a const char *
, is going to be undefined behavior. Newer APIs may prefer std::string_view
for those cases.
更新:
通常,假设一个C ++ API带有一个接受NULL的指针,这是很公平的.但是,C字符串是一种特殊情况.在std::string_view
之前,没有更好的方法可以有效地传递它们.通常,对于接受const char *
的API,应该假定它必须是有效的C字符串.即指向以'\ 0'结尾的char
序列的指针.
Usually it is fair to assume a C++ API taking a pointer to accept NULL. However C strings are a special case. Until std::string_view
, there was no better way to pass them efficiently. In general for an API accepting const char *
, assumption should be is that it has to be a valid C string. i.e a pointer to a sequence of char
s that terminates with a '\0'.
range_error
可以验证指针不是nullptr
,但不能验证指针是否以'\ 0'结尾.因此最好不要进行任何验证.
range_error
could validate that the pointer is not nullptr
but it cannot validate if it terminates with a '\0'. So it better not do any validation.
我不知道标准中的确切措词,但是该前提条件可能是自动假定的.
I don't know the exact wording in the standard, but this pre-condition is probably assumed automatically.
这篇关于使用空指针参数和不可能的后置条件构造标准异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!