C++ 流提取运算符重载

C++ stream extraction operator overloading

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

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

应该是用户编写的提取运算符 (>>)

  1. 设置失败标志(因为找不到所需对象的实例)
  2. 是否应该 return 给仍然设置了 eof 标志的调用者。

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

原代码如下。根据下面的评论,这段代码似乎是错误的。如果 eof 已经在输入上设置,提取运算符将简单地 return 并且 eof 仍然设置。似乎如果设置了 eof,但未设置 bad 和 fail,则应提取字符串以设置失败位。当然也可以直接设置fail位。

/* 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;
}

"If the second approach is used, it implies that the caller must always check the eof flag before any attempt is made to invoke the >> operator."

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

"Should a user-written extraction operator (>>) set the failure flag (because no instance of the desired object can be found) or should it just return to the caller with the eof flag still set."

当然,后一种选择是,您不应该在重载的提取运算符中管理流状态,除非您添加自己的验证规则(例如,期望使用 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>>() 失败,因为流处于 eof() 状态,并且该值将保留其原始值试图提取的字段的状态。

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

客户端(调用者)将简单地使用例如

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

你看,不需要在任何地方检查 input.eof()

您应该实施解决方案 1。

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

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

failbit 如果 由于任何原因 operator>> 实际上没有流式传输值,则应设置

failbit。 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;
  }
}