可以将std::getline()与从std::string中移出的字符串一起使用吗

Ok to use std::getline() with a moved-from std::string?

本文关键字:std 字符串 一起 移出 与从 getline string      更新时间:2023-10-16

std::getline(std::istream&, std::string&)的第二个参数是引用从std::string移动的的左值是否安全且定义良好?如果是,该字符串是否从其从移动的状态恢复,以便可以安全地调用pop_back()等方法?

更简单地说,用getline()写入字符串是否与分配给该字符串具有等效的语义?

或者更具体地说,以下(有些做作的)片段定义明确且正确吗?

std::ifstream f("foo.txt");
std::vector<std::string> lines;
for (std::string s; std::getline(f, s); lines.push_back(std::move(s)))
if (!s.empty() && s.back() == 'r')
s.pop_back();

使用g++clang++对该代码段进行优化(-march=native -O3)构建似乎可以按预期工作,但这当然不能保证。

我想知道这是否只依赖于根据C++11标准中getline()的语义定义良好的行为,或者,如果不是,它是否由该标准的后续版本定义良好,或者,否则,它是否至少由任何/所有主要实现(G++、Clang++、Visual C++、英特尔C++编译器)明确定义。

注意:这是而不是之前问题的重复,该问题询问分配给从对象是否安全(是的,如果是琐碎类型或STL类型),因为getline()不是赋值运算符。

您的代码是安全的,但这只是因为您正在检查getline是否成功地将某些内容存储在s中。如果getline内部的哨兵未能初始化,则不会向s分配任何内容,并且它将处于有效但未指定的状态。只要getline成功地将s设置为已知状态,就可以了。

并且getline在成功构建哨兵之后所做的第一件事就是将s设置为零大小(已知状态)。

更多详情请参阅GManNickG的评论。

std::getline(std::istream&, std::string&)的第二个参数是引用从std::string移动的

是。尽管如此,根据[string.cons]p2:的标准,移动的字符串未指定

[…]。在第二种形式中,str处于有效状态,但具有未指定的值。

字符串仍然处于有效状态,只是未指定,即您不知道它处于哪个状态(中可能填充了一些随机字符)。您仍然可以将它传递给std::getline,因为标准在[string.io]p1中说,str.erase()是在字符串上调用的,这会清空它:

[…]调用str.erase()[…]

(如果流是有效的,但我认为这是您的情况)。

无论字符串的未指定状态是什么,它都会在std::getline开始时清空,所以它处于什么状态都无关紧要。

移动赋值或移动c'tor应始终使移动的对象处于有效状态。

在std::字符串的情况下,当您std::move它时,它将保持为有效但未指定的字符串。来自libc++的来源:

// __str is a valid, but unspecified string. 
basic_string(basic_string&& __str) noexcept
: _M_dataplus(_M_local_data(), std::move(__str._M_get_allocator()))

由于getline()只在字符串中添加字符,因此只要成功,输出就会如预期的那样。来自cplusplus.com:

请注意,调用之前str中的任何内容都将被新提取的序列替换。

每个提取的字符都被附加到字符串中,就好像调用了其成员push_back一样。