【发布时间】:2020-02-13 16:44:35
【问题描述】:
一位同事想写这个:
std::string_view strip_whitespace(std::string_view sv);
std::string line = "hello ";
line = strip_whitespace(line);
我说返回string_view 让我感到不安先验,而且这里的别名在我看来就像是UB。
我可以肯定地说line = strip_whitespace(line) 在这种情况下等同于line = std::string_view(line.data(), 5)。我相信会调用string::operator=(const T&) [with T=string_view],它被定义为等同于line.assign(const T&) [with T=string_view],它被定义为等同于line.assign(line.data(), 5),它被定义为这样做:
Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.
但这并没有说明出现混叠时会发生什么。
我昨天在 cpplang Slack 上问了这个问题,得到的答案好坏参半。在这里寻找超级权威的答案,和/或对真实库供应商实施的实证分析。
I wrote test cases 代表string::assign、vector::assign、deque::assign、list::assign 和 forward_list::assign。
- Libc++ 使所有这些测试用例都能正常工作。
- Libstdc++ 使它们都可以工作,除了
forward_list,它会出现段错误。 - 我不知道 MSVC 的库。
libstdc++ 中的段错误让我希望这是 UB;但我也看到 libc++ 和 libstdc++ 至少在常见情况下会付出巨大努力。
【问题讨论】:
-
您是否使用 ASan 编译测试用例和/或在 Valgrind 下运行它们?这将消除代码是否会导致访问冲突的猜测,尽管它可能仍然在实践中起作用,而不是按照定义。
-
"如果 basic_string 的任何成员函数或运算符抛出异常,则该函数或运算符对 basic_string 对象没有其他影响。" -- 这会强制在现有存储被释放之前进行存储分配,因此如果分配失败,则会引发异常,而不会更改
*this。但我认为没有什么可以阻止现有存储被重用,在这种情况下,这变得未指定,因为复制存储的语义是未指定的。 -
对于提到的序列容器,肯定是UB,因为在[tab:container.seq.req]中违反了
assign的前提条件。
标签: c++ stl undefined-behavior