好的,使用带有移动的std :: string的std :: getline()?

时间:2017-10-10 18:27:51

标签: c++ c++11 stl c++14 c++17

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();

-march=native -O3g++此代码段的优化(clang++)版本似乎按预期工作,但这当然不能保证。

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

注意:这是 与以前的问题重复,询问是否可以安全地分配给移动的对象(是的,如果它&#39 ; sa琐碎或STL类型)因为getline()不是赋值运算符。

3 个答案:

答案 0 :(得分:8)

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

成功完成哨兵训练后getline的第一件事就是将s设为零(一个已知状态)。

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

答案 1 :(得分:5)

  

std::getline(std::istream&, std::string&)的第二个参数是否安全且定义明确是一个引用移动std::string

的左值

是。虽然根据[string.cons]p2标准,未移动的字符串未指定:

  

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

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

  

[...]致电str.erase() [...]

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

无论字符串的未指定状态是什么,它都会在std::getline的开头清空,因此它处于什么状态并不重要。

答案 2 :(得分:1)

移动分配或移动c应始终使移动的对象保持有效状态。

如果是std :: string,当它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一样。