【问题标题】:C++ stream extraction operator overloadingC++ 流提取运算符重载
【发布时间】:2015-06-03 18:40:52
【问题描述】:

这是关于用户编写的 C++ 输入流提取运算符 (>>) 的哲学(规范设计)的问题。

假设在进入 >> 运算符实现时(对于用户编写的类),已经为输入流设置了 eof 标志。

应该是用户编写的提取操作符(>>)

  1. 设置失败标志(因为找不到所需对象的实例)
  2. 是否应该在仍设置了 eof 标志的情况下返回给调用方。

如果使用第二种方法,则意味着调用者必须始终检查 eof 标志,然后再尝试调用 >> 运算符。原因是 >> 运算符可能会成功提取所需类的实例并设置 eof 标志。

原始代码如下。根据下面的 cmets,此代码似乎是错误的。如果 eof 已在输入中设置,则提取运算符将简单地返回,但仍设置 eof。似乎如果设置了 eof,但未设置 bad 和 fail,则应提取字符串以设置失败位。当然也可以直接设置fail bit。

/* Implement the C/C++ >> (stream input) operator as a non-member 
   function */
std::istream &operator>>(std::istream& is, DecNumber &val) {
  DecContext  context{DecContext::defInit};
  uint32_t    status;
  /* The true value below prevents whitespace from being skipped */
  std::istream::sentry  s(is, true);
  std::string           inStr;
  /* Check if the input stream is in a good state. Just return to the
     caller if the input stremm is not in a good state. The caller
     must handle this condition. */
  if(!s) 
    return is; 
  /* Get a string from the input stream. This string is converted to 
     a DecNumber below. Just return to the caller if this step causes
     any stream related errors. Note that reaching the end of the 
     input is not a stream related error here. A decimal number might
     be the absolute last thing in the stream. */
  is >> inStr;
  if (is.bad() || is.fail())
    return is;
  /* Try to convert the string to a DecNumber using the default context
     value */
  decNumberFromString(val.getDecVal(), inStr.c_str(), context.getDecCont());
  status = context.DecContextGetStatus();
  /* Remove a few status bits we don't care about */
  status &= ~(DEC_Inexact + DEC_Rounded);
  if (status)
    is.setstate(std::ios_base::failbit); 
  return is;
}

【问题讨论】:

  • 写一个concise sample 来澄清你真正想要的是什么!如前所述,我已经用尽了我对这个问题的密切投票,但除非你在你的问题中表明这一点,否则它要么过于宽泛,要么实际上基于意见
  • πάντα ῥεῖ,我的目的是获得有关流提取运算符 (>>) 的规范形式如何处理在输入时存在或在提取过程中发生的 eof 条件的建议.许多人(包括您)对解决这个问题非常有帮助。
  • 没有规范的形式,但用例可以用于自定义重载。抱歉,c++ 不提供烹饪食谱,而是提供灵活性。

标签: c++ overloading operator-keyword


【解决方案1】:

您应该实施解决方案 1。

如有疑问,请查看已完成的工作。如下所示,如果我们尝试从 EOF 状态的流中读取,则会设置失败位。

请注意,EOF 并不是唯一失败的方法。尝试在下面的代码中设置std::string vals = "52 43 A";

failbit 如果出于任何原因 应设置,operator>> 实际上并未流式传输值。 EOF 只是其中一个原因。

#include <sstream>
#include <iostream>
#include <string>

void print_stream (std::istream & print_me, int const & i)
{
  std::cout << "i: " << i << "\n";
  std::ios_base::iostate bits = print_me.rdstate();

  std::cout << "good: " << (bits & std::ios_base::goodbit) << 
    ", bad: " << (bits & std::ios_base::badbit) << 
    ", fail: " << (bits & std::ios_base::failbit) <<
    ", eof: " << (bits & std::ios_base::eofbit) << "\n";

  std::cout << "\n----------------------------\n\n";
}

int main (void)
{
  std::string vals = "52 43";
  std::istringstream iss(vals);
  int i;

  iss >> i;
  print_stream (iss, i);
  iss >> i;
  print_stream (iss, i);
  iss >> i;
  print_stream (iss, i);
  iss >> i;
  print_stream (iss, i);

  return 0;
}

输出

$ ./a.exe
i: 52
good: 0, bad: 0, fail: 0, eof: 0

----------------------------

i: 43
good: 0, bad: 0, fail: 0, eof: 2

----------------------------

i: 43
good: 0, bad: 0, fail: 4, eof: 2

----------------------------

i: 43
good: 0, bad: 0, fail: 4, eof: 2

----------------------------

请注意,典型的读取模式循环是...的一些变体

while (input >> var >> var2 >> var3)
{
  // failbit is not set.  All reads succeeded.
  // Do Stuff
}

如果您需要在读取多个值的过程中检测失败是否发生在某个时间点,那么是的,您需要更复杂一些并进行一些测试,例如...

while (true)
{
  if (input >> var)
  {
    // We successfully read first value
    if (input >> var2 >> var3)
    {
      // We succesfully read all the values!
      // Do stuff
    }
    else
    {
      ErrorLog ("Partial line read!");
      break;
    }
  else
  {
    // Nothing else to read
    break;
  }
}

【讨论】:

  • 重载的提取操作符在哪里??你的回答对我来说没有任何意义。
  • QuestionC 您的代码示例和 cmets 提供了我在尝试实现流提取运算符时所缺少的见解。 πάντα ῥεῖ 即使我不完全同意,您在这件事上的努力也值得称赞。
【解决方案2】:

“如果使用第二种方法,则意味着调用者必须始终检查 eof 标志,然后再尝试调用 >> 运算符。”

不,你认为他们为什么需要这样做?

“应该由用户编写的提取操作符 (>>) 设置失败标志(因为找不到所需对象的实例),还是应该在 eof 标志仍然设置的情况下返回给调用者。”

当然,后一种选择,您不应该在重载的提取运算符中管理流状态,除非您添加自己的验证规则(例如,使用 std::string 字段来期待特定的字符模式)。它通常可以通过重载运算符使用的子提取操作正确完成。

假设您有以下内容:

struct MyType {
    std::string field1;
    int field2;
    double field3;
}

std::istream& operator>>(std::istream& is, MyType& myinstance) {
      is >> field1;
      is >> field2;
      is >> field3;
      return is;
}

每次提取都会将字段设置为其默认构造值,以防 operator&gt;&gt;() 失败,因为流处于 eof() 状态,并且该值将保留为字段的原始状态试图提取。

实际上,我认为不需要在重载的输入运算符中对 eof() 或将流设置为 fail() 状态进行任何额外检查。

客户端(调用者)将简单地使用类似的东西,例如

 std::ifstream input("MyFile.txt");
 std::vector<MyType> allObjects;
 MyType curObject;
 while(input >> curObject) {
      allObjects.push_back(curObject);
 }

您看,无需在任何地方检查input.eof()

【讨论】:

  • 重载的提取运算符不一定只是简单成员提取的集合。它可能需要进行一些自定义验证。如果验证失败,则应在流上设置失败位。
  • @BenjaminLindley 好吧,OP 应该更多地澄清他们当时实际在做什么。我已经开始对这个问题进行近距离投票,主要基于意见(稍后收回)。 OP 只是谈论处理eof() 状态,在这种情况下,当然没有必要操纵输入流状态。对可操作的std::istream 进行自定义验证是从重载运算符操纵流状态的正当理由。拥有eof() 状态,不是。
  • 我真的只是在回答你的回答中的一个特定点,“你不应该在重载的提取操作符中管理流状态,它将通过子提取操作正确完成重载的运算符使用。” -- 也就是说,根据我之前的评论,imo 是错误的。不管 OP 在做什么。
  • πάντα ῥεῖ,为什么用户需要在使用 C++ 输入流提取运算符 (>>) 之前和之后检查 eof?因为否则,用户将无法判断在使用 >> 运算符之前是否存在 eof 状态,或者是否由于使用 >> 运算符从输入流中获取值(很可能有效)而发生 eof 状态.
  • @PeterSchaeffer 这对用户来说真的很重要吗,为什么提取实际上失败了?如果是因为eof,或者实际上是解析流时提取失败?无论哪种方式,流都会进入fail 状态,除非您想对标准提取运算符成功解析的字段进行额外验证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-10
  • 2012-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多