使用空指针参数和不可能的后置条件构造标准异常


9

考虑以下程序:

#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 ++ segfaults构造异常的Clang。

godbolt

编译器的行为符合标准吗?标准[diagnostics.range.error](C ++ 17 N4659)的相关部分确实说std::range_errorconst char*构造函数重载,应该优先于const std::string&重载。本节也没有说明构造函数的任何前提条件,而只说明了后置条件

后置条件strcmp(what(), what_­arg) == 0

如果what_arg为空指针,则此后置条件始终具有未定义的行为,那么这是否意味着我的程序也具有未定义的行为并且两个编译器的行为一致?如果没有,应该如何阅读标准中如此不可能的后置条件?


再次考虑,我认为这一定意味着程序的未定义行为,因为如果不这样做,那么(有效)不指向以null终止的字符串的指针也将被允许,这显然没有意义。

因此,假设这是真的,我想将问题更多地集中在标准如何暗示这种未定义的行为上。是由于后置条件的不可能而导致调用也具有未定义的行为,还是先决条件被简单地忘记了?


受到这个问题的启发。


听起来std :: range_error允许通过引用存储事物,所以我不会感到惊讶。传递what()何时调用nullptr可能会导致问题。
Chipster

@Chipster我不确定您的确切意思,但是在再次考虑之后,我认为它一定是未定义的行为。我已经对问题进行了编辑,以使其更多地关注标准措辞如何暗示未定义的行为。
胡桃木

如果 nullptr通过了,我认为what()必须在某个时候取消引用它以获得值。那将取消引用nullptr,这充其量是有问题的,并且肯定会崩溃是最坏的情况。
Chipster

不过,我同意。它必须是未定义的行为。但是,将其放入一个适当的答案中以解释原因超出了我的能力范围。
Chipster

我认为这是参数指向有效C字符串的前提,因为strcmp它用于描述的值what_arg。无论如何,这就是C标准的相关部分所说的,这由的规范所引用<cstring>。当然措词可能更清楚。
LF

Answers:


0

文档

因为不允许复制std :: range_error引发异常,所以此消息通常在内部存储为单独分配的引用计数字符串。这也是为什么没有构造函数采用std :: string &&的原因:无论如何它都必须复制内容。

这说明了为什么会出现段错误,API确实将其视为真实字符串。通常,在cpp中,如果某些东西是可选的,则将有一个重载的构造函数/函数,它不需要任何不需要的内容。因此,传递nullptr不记录某些可选内容的功能将是未定义的行为。通常,API不使用C字符串除外的指针。因此,恕我直言,可以安全地假设为期望a的函数传递nullptr const char *将是未定义的行为。较新的API可能std::string_view适合这些情况。

更新:

通常,假定C ++ API带有接受NULL的指针是公平的。但是,C字符串是一种特殊情况。直到std::string_view,没有更好的方法可以有效地传递它们。通常,对于API接受const char *,应该假设它必须是有效的C字符串。即指向以char'\ 0'结尾的s 序列的指针。

range_error可以验证指针不是,nullptr但是不能验证其是否以“ \ 0”结尾。因此最好不要进行任何验证。

我不知道标准中的确切措词,但是该前提条件可能是自动假定的。


-2

回到基本问题,是否可以从nullptr创建std :: string?它应该做什么?

www.cplusplus.com

如果s是一个空指针,或者如果n == npos,或者如果[first,last)指定的范围无效,则它将导致未定义的行为。

所以什么时候

throw std::range_error(nullptr);

被称为实现试图使像

store = std::make_shared<std::string>(nullptr);

这是未定义的。我会考虑的错误(没有阅读标准中的实际措辞)。相反,自由主义者的开发商本可以做出类似

if (what_arg)
  store = std::make_shared<std::string>(nullptr);

但是随后捕获程序必须在其中检查nullptr,what();否则它将在那里崩溃。因此,std::range_error应该像其他语言一样分配空字符串或“(nullptr)”。


“这回到了基本问题,是否可以从nullptr创建std :: string?” -我认为没有。
Konrad Rudolph

我认为该标准没有规定必须在其中保存异常的任何地方,std::string并且std::string不应通过重载决议选择构造函数。
核桃

const char *过载是通过重载解析选择
MM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.